反射机制
反射机制
类对象
所有的类,都存在一个类对象,这个类对象用于提供类本身的信息,比如有几种构造方法, 有多少属性,有哪些普通方法。通过class实例获取class信息的方法就是反射
获取类对象有3种方式
- Class.forName
- 类名.class
- new 类.getClass()
在一个JVM中,一种类,只会有一个类对象存在。所以以上三种方式取出来的类对象,都是一样的
获取类对象的时候,会导致类属性被初始化
静态方法被修饰为synchronized的时候,其同步对象就是当前类的类对象
创建对象
与传统的通过new 来获取对象的方式不同,反射机制会先拿到Hero的“类对象”,然后通过类对象实例化一个对象构造器,再通过构造器对象创建一个对象
String className = "类所在位置";
//类对象
Class pClass=Class.forName(className);
//构造器
Constructor c= pClass.getConstructor();
//通过构造器实例化
Hero h2= (Hero) c.newInstance();
访问字段
通过Field实例可以读取或设置某个对象的字段,如果存在访问限制,要首先调用setAccessible(true)
来访问非public
字段。
//获取类Hero的名字叫做name的字段
Field f1= h.getClass().getDeclaredField("name");
//修改这个字段的值,第一个Object参数是指定的实例,第二个Object参数是待修改的值
f1.set(h, "teemo");
Field getField(name):根据字段名获取某个public的field(包括父类)
Field getDeclaredField(name):根据字段名获取当前类的某个field(不包括父类)
Field[] getFields():获取所有public的field(包括父类)
Field[] getDeclaredFields():获取当前类的所有field(不包括父类)
getField和getDeclaredField的区别:
- getField 只能获取public的,包括从父类继承来的字段。
- getDeclaredField 可以获取本类所有的字段,包括private的,但是不能获取继承来的字段。 (注: 这里只能获取到private的字段,但并不能访问该private字段的值,除非加上setAccessible(true))
一个Field
对象包含了一个字段的所有信息:
getName()
:返回字段名称,例如,"name"
;getType()
:返回字段类型,也是一个Class
实例,例如,String.class
;getModifiers()
:返回字段的修饰符,它是一个int
,不同的bit表示不同的含义。
获取一个实例对应的该字段的值:
先获取Class
实例,再获取Field
实例,然后,用Field.get(Object)
获取指定实例的指定字段的值。
调用方法
// 获取这个名字叫做setName,参数类型是String的方法
Method m = h.getClass().getMethod("setName", String.class);
// 对h对象,调用这个方法,对Method实例调用invoke就相当于调用该方法,invoke的第一个参数是对象实例,即在哪个实例上调用该方法,后面的可变参数要与方法参数一致
m.invoke(h, "盖伦");
Class
类提供了以下几个方法来获取Method
:
Method getMethod(name, Class...)
:获取某个public
的Method
(包括父类)Method getDeclaredMethod(name, Class...)
:获取当前类的某个Method
(不包括父类)Method[] getMethods()
:获取所有public
的Method
(包括父类)Method[] getDeclaredMethods()
:获取当前类的所有Method
(不包括父类)
一个Method
对象包含一个方法的所有信息:
getName()
:返回方法名称,例如:"getScore"
;getReturnType()
:返回方法返回值类型,也是一个Class实例,例如:String.class
;getParameterTypes()
:返回方法的参数类型,是一个Class数组,例如:{String.class, int.class}
;getModifiers()
:返回方法的修饰符,它是一个int
,不同的bit表示不同的含义。
使用反射调用方法时,仍然遵循多态原则:即总是调用实际类型的覆写方法(如果存在)
调用构造方法
调用非public
的Constructor
时,必须首先通过setAccessible(true)
设置允许访问
通过Class实例获取Constructor的方法如下:
getConstructor(参数类型)
:获取某个public
的Constructor
;getDeclaredConstructor(参数类型)
:获取某个Constructor
;getConstructors()
:获取所有public
的Constructor
;getDeclaredConstructors()
:获取所有Constructor
。
然后调用newInstance(Object... parameters)创建实例对象
获取继承关系
通过Class
对象可以获取继承关系:
Class getSuperclass()
:获取父类类型;Class[] getInterfaces()
:获取当前类实现的所有接口
动态代理
在运行期动态创建一个interface
实例的方法如下:
- 定义一个
InvocationHandler
实例,它负责实现接口的方法调用; - 通过Proxy.newProxyInstance()创建interface实例,它需要3个参数:
- 使用的
ClassLoader
,通常就是接口类的ClassLoader
; - 需要实现的接口数组,至少需要传入一个接口进去;
- 用来处理接口方法调用的
InvocationHandler
实例。
- 使用的
- 将返回的
Object
强制转型为接口。
InvocationHandler handler = new InvocationHandler() {
@Override
/*
* proxy:把代理对象自身传递进来
* method:代表当前调用的方法
* args:调用方法的参数
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(method);
if (method.getName().equals("morning")) {
System.out.println("Good morning, " + args[0]);
}
return null;
}
};
/*
* 第一个参数:为了产生某个对象的代理对象,需要一个类装载器。(固定写法)
* 第二个参数:产生谁的代理对象。(基于接口进行代理,所以要拿到接口)
* 第三个参数:产生的代理对象干什么事情,什么事情是通过一个对象来指定的,也就是说这个地方必须要new一个InvocationHandler接口类型的对象
*/
Hello hello = (Hello) Proxy.newProxyInstance(
Hello.class.getClassLoader(),
new Class[] { Hello.class },
handler);
hello.morning("Bob");
interface Hello {
void morning(String name);
}