详解Java反射机制
一、什么是JAVA的反射机制
Java反射是Java被视为动态(或准动态)语言的一个关键性质。这个机制允许程序在运行时透过Reflection APIs取得任何一个已知名称的类的内部信息,包括其方法(诸如public, static 等)、父类(例如Object)、实现接口(例如Cloneable),也包括属性和方法的所有信息,并可于运行时改变属性值或调用方法。
Java反射机制容许程序在运行时加载、探知、使用编译期间完全未知的类。Java可以加载一个运行时才得知名称的类,获得其完整结构。
简言之,JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
二、JDK中提供的Reflection API
Java反射相关的API在包java.lang.reflect中,JDK 1.6.0的reflect包如下图:
该类下的几个主要类和接口
三、JAVA反射机制提供了什么功能
=========================================
Java反射机制提供如下功能(用途):
在运行时判断任意一个对象所属的类
在运行时构造任意一个类的对象
在运行时判段任意一个类所具有的成员变量和方法
在运行时调用任一个对象的方法
在运行时创建新类对象
=======================================
一个对象的创建分三步完成:
1、加载类到内存;
2、静态资源初始化;
3、创建对象;
一个类对应一个字节码文件*.class,反射研究的就是字节码文件;一个字节码文件中主要包含类的成员变量、方法、构造方法;通过该字节码文件可以获取该类的相关信息。同一个字节码文件可以构建多个对象;
//类的字节码文件
MyA a1=new MyA();
//获得该类的字节码文件
Class clazz=a1.getClass();
对象类型有字节码文件,基础的数据类型也有相应的字节码文件。
int.class
void.class
在使用Java的反射功能时,基本首先都要获取类的Class字节码对象,再通过Class对象获取其他的对象。
1、获取类的Class字节码对象
Class 类的实例表示正在运行的 Java 应用程序中的类和接口。获取类的Class对象有多种方式:
2、获取类的属性
可以通过反射机制得到某个类的某个属性,然后可以改变对应于这个类的某个实例的该属性值。JAVA 的Class<T>
类提供了几个方法获取类的属性。
public Field getField(String name)
返回某类一个public修饰的成员变量(public,包括父类中定义);
public Field[] getFields()
返回一个数组,数组中包含某类下的public修饰的所有成员变量(public,包括父类中定义);
public Field getDeclaredField(String name)
返回某类一个private修饰的成员变量(private,不包括父类中定义);
public Field[] getDeclaredFields()
返回一个数组,数组中包含某类下的所有private修饰的成员变量(private,不包括父类中定义);
3、获取类的方法
通过反射机制得到某个类的某个方法,然后可以调用对应于这个类的某个实例的该方法,Class<T>
类提供了几个方法获取类的方法。
public Method getMethod(String name,Class<?>... parameterTypes)
通过方法名和方法的入参得到一个某类下的public修饰的Method方法对象(public,包括父类中定义);
public Method[] getMethods()
得到某类下的public修饰的所有Method方法对象(public,包括父类定义的)
public MethodgetDeclaredMethod(Stringname,Class<?>... parameterTypes)
通过方法名和方法的入参得到一个某类下的private修饰的Method方法对象(private,不包括父类中定义);;
public Method[] getDeclaredMethods()
得到某类下的private修饰的所有Method方法对象(private,不包括父类定义的)
通过反射获得类的方法(public)
MyA m1=new MyA();
Class clazz=m1.getClass();
//方法名,入参
clazz.getMethod("setAge",int.class);
//获得方法名之后,如何调用该方法(对象,入参)
setAge.invoke(m1,23);//调用m1下的该方法
m1.getAge();//23
注意:
setAge.invoke(null,23);
当前类下的不依赖对象的方法(静态方法)要被执行;
4、获取类的Constructor构造方法
通过反射机制得到某个类的构造器,然后可以调用该构造器创建该类的一个实例Class<T>
类提供了几个方法获取类的构造器。
public Constructor<T> getConstructor(Class<?>... parameterTypes)
通过构造方法的入参返回一个有相应入参的 Constructor 对象(public,包括父类中定义);
public Constructor<?>[] getConstructors()
返回一个数组,该数组中包含该类下的所有构造方法(public,包括父类中定义);
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
通过构造方法的入参返回一个有相应入参的 Constructor 对象(private,不包括父类中定义);
public Constructor<?>[] getDeclaredConstructors()
返回一个数组,该数组中包含该类下的所有构造方法(private,不包括父类中定义);
5、创建类的实例
通过反射机制创建类的实例,有几种方法可以创建
调用无自变量的构造器
1、调用类的Class对象的newInstance方法,该方法会调用对象的默认构造器,如果没有默认构造器,会调用失败(所以,在实际应用中用到这种方法时,如果要重写一个类的带参数的构造方法,就一定要把不带参数的构造方法显式定义出来).
Class<?> classType = ExtendType.class;
Object inst = classType.newInstance();
System.out.println(inst);
输出:
Type:Default Constructor
ExtendType:Default Constructor
com.quincy.ExtendType@d80be3
2、调用默认Constructor对象的newInstance方法
Class<?> classType = ExtendType.class;
Constructor<?> constructor1 = classType.getConstructor();
Object inst = constructor1.newInstance();
System.out.println(inst);
输出:
Type:Default Constrructor
ExtendType:Default Constru
com.quincy.ExtendType@1006d756d75
调用带参数构造器
3、调用带参数Constructor对象的newInstance方法
Constructor<?> constructor2 =
classType.getDeclaredConstructor(int.class, String.class);
Object inst = constructor2.newInstance(1, "123");
System.out.println(inst);
输出:
Type:Default Constructor
ExtendType:Constructor with parameters
com.quincy.ExtendType@15e83f9
6、调用类的函数
通过反射获取类Method方法对象,调用Field的Invoke方法调用函数(方法)。
参见上面获取类的方法;
7、设置/获取类的属性值
通过反射获取类的成员变量,调用成员变量的方法设置或获取值;
MyA m1=new MyA("zhangsan");
MyA m2=new MyA("lisi");
Class clazz=m1.getClass();
//获得MyA类的所有成员变量
Field[] fields=clazz.getFields();
//获得MyA下名为"name"的成员变量
Field nameField=clazz.getField("name");
以上适用于用public修饰的成员变量,如果成员变量使用private修饰的则拿不到;要用以下方法:
//nameField是一个引用(地址)
Field nameField=clazz.getDeclaredField("name");
//如果该成员变量使用private修饰的,要加下面一句
nameField.setAccessible("true");
------------------要指定获得哪一个对象上的数据
//获得m1中nameField对应的成员变量的名字
String name=nameField.get(m1);
//获得m2中nameField对应的成员变量的名字
String name=nameField.get(m2);
--------------------为m1中的成员变量设值
nameField.set(m1,"张三");//覆盖掉zhangsan
//获得nameField对应的成员变量的类型
nameField.getType();
四、动态创建代理类
代理模式:代理模式的作用=为其他对象提供一种代理以控制对这个对象的访问。
代理模式的角色:
抽象角色:声明真实对象和代理对象的共同接口
代理角色:代理角色内部包含有真实对象的引用,从而可以操作真实对象。
真实角色:代理角色所代表的真实对象,是我们最终要引用的对象。
动态代理::
java.lang.reflect.Proxy
Proxy 提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类
InvocationHandler
是代理实例的调用处理程序实现的接口,每个代理实例都具有一个关联的调用处理程序。对代理实例调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序的 invoke 方法。
动态Proxy是这样的一种类:
它是在运行生成的类,在生成时你必须提供一组Interface给它,然后该class就宣称它实现了这些interface。你可以把该class的实例当作这些interface中的任何一个来用。当然,这个Dynamic Proxy其实就是一个Proxy,它不会替你作实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工作。
在使用动态代理类时,我们必须实现ndler接口
有关代理模式的具体了解请参看本人设计模式相关文章。
补充:
1、
`MyA a1=new MyA();
Class clazz=a1.getClass();
MyA a2=new MyA();
if(a1 == a2)…//不相等,比较的是引用(地址)
if(clazz == a2.getClass())…//相等(是由同一个字节码文
件构建的,都相当于:new MyA().getClass())
`
2、拿到一个字节码文件之后,如何判断它是一个对象类型还是普通的数据类型?
//基础数据类型返回true,对象类型返回false
clazz.isPrimitive();//false
int.class.inPrimitive();//true