在很多情况下,我们知道如何编写反射相关类或者方法,但是无法口述反射是什么,里面的机制是什么,下面我先如浅入深介绍反射。
1、定义
反射 (Reflection) 是 Java 的特征之一,它允许运行中动态加载Java 程序获取自身的信息,并且可以操作类或对象的内部属性,是一种功能强大且复杂的机制,可以用在下面四个方面:
- 在运行时分析类的能力
- 在运行时查看对象
- 实现通用的数组操作代码
- 利用Method对象
上面是正式的反射定义,比较抽象,用个大白话来说,反射就是动态加载类、调用方法和访问属性,它不需要事先知道运行对象是谁。反射的重点在运行时而不是编译时,也就是动态加载。下面我们先来看下一个简单的例子
// 普通new
User user = new User();
user.setEmail("xxxx@xxx.com");
// 反射例子
try {
Class clz = Class.forName("com.fomin.demo.model.User");
Method method = clz.getMethod("setEmail", String.class);
Constructor constructor = clz.getConstructor();
Object object = constructor.newInstance();
method.invoke(object, "aaa@xx.com");
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException
| InstantiationException | InvocationTargetException e) {
e.printStackTrace();
}
上面需要一个User实例,正常情况下,我们只需要new一个实例就行,但我们也会遇到一些特殊情况,我不知道初始化的类对象是什么,无法使用new进行实例化,这就需要在动态运行时才需要创建一个实例。
2、用法
Java的反射机制的实现要借助于4个类:Class,Constructor,Field,Method,并且如果需要创建实例,可以使用newInstance创建,例如,clz.newInstance或者constructor.newInstance
- Class:代表的是类对象,反射的核心。在反射中,首先需要获取到该类的Class对象。有三种实现方式
//使用 Class.forName 静态方法
Class clz = Class.forName("com.fomin.demo.model.User");
//使用 .class 方法
Class clz = User.class;
//使用类对象的 getClass() 方法
User user = new User();
Class clz = user.getClass();
- Constructor:类的构造器对象,用来获取构造函数方法,使用方式有以下几种:
//获取指定参数获得public构造函数
Constructor<T> getConstructor(Class<?>... parameterTypes);
//根据指定参数获取public和非public的构造函数
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes);
- Field:类的属性对象
//获取类的所有声明字段,但不能得到其父类的成员变量,包含私有的
Field[] getDeclaredFields()
//获取指定参数的类的所有声明字段,但不能得到其父类的成员变量,包含私有的
Field getDeclaredField(String name)
//获取类的所有公有声明字段,但不能得到其父类的成员变量
Field[] getFields()
//获取指定参数的类的所有公有声明字段,但不能得到其父类的成员变量
Field getField(String name)
- Method:类的方法对象
//获取类的所有公用(public)方法,包括其继承类的公用方法
Method[] getMethods()
//获取特定的公用(public)方法,第一个参数为方法名称,后面的参数为方法的参数对应Class的对象
Method getMethod(String name, Class<?>... parameterTypes)
//获取类或接口声明的所有方法,包括公共、保护、默认访问和私有方法,不包括继承方法
Method[] getDeclaredMethods()
//获取特定的方法,第一个参数为方法名称,后面的参数为方法的参数对应Class的对象
Method getDeclaredMethod(String name, Class<?>... parameterTypes)
**3、**invoke:
属于Method的一个方法,用来反射执行方法的,包含两个参数,一个是Object obj表示被调用方法底层所属对象,Object… args表示调用方法是传递的实际参数。
invoke中核心在MethodAccessorImpl,NativeMethodAccessorImpl,DelegatingMethodAccessorImpl这三个类中,其中NativeMethodAccessorImpl和DelegatingMethodAccessorImpl继承了抽象类MethodAccessorImpl。并且 NativeMethodAccessorImpl 对象交给 DelegatingMethodAccessorImpl 对象代理。
在执行invoke时,首先需要对非重写方法检查权限,对非public方法,进行二次检查checkAccess。
if (!override) {
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
checkAccess(caller, clazz, obj, modifiers);
}
}
获取当前的MethodAccessor,如果是空的,调用acquireMethodAccessor创建,首先先从root获取,还是为空调用ReflectionFactory.newMethodAccessor创建一个,并设置在root中。
MethodAccessor tmp = null;
if (root != null) tmp = root.getMethodAccessor();
if (tmp != null) {
methodAccessor = tmp;
} else {
// Otherwise fabricate one and propagate it up to the root
tmp = reflectionFactory.newMethodAccessor(this);
setMethodAccessor(tmp);
}
最后调用NativeMethodAccessorImpl中的native方法invoke0
public Object invoke(Object var1, Object[] var2) throws IllegalArgumentException, InvocationTargetException {
if (++this.numInvocations > ReflectionFactory.inflationThreshold() && !ReflectUtil.isVMAnonymousClass(this.method.getDeclaringClass())) {
MethodAccessorImpl var3 = (MethodAccessorImpl)(new MethodAccessorGenerator()).generateMethod(this.method.getDeclaringClass(), this.method.getName(), this.method.getParameterTypes(), this.method.getReturnType(), this.method.getExceptionTypes(), this.method.getModifiers());
this.parent.setDelegate(var3);
}
return invoke0(this.method, var1, var2);
}
void setParent(DelegatingMethodAccessorImpl var1) {
this.parent = var1;
}
private static native Object invoke0(Method var0, Object var1, Object[] var2);
4、总结
反射使用的地方很多,例如AOP思想就是需要用到发射、模块化的开发,通过反射去调用对应的字节码;动态代理设计模式也采用了反射机制, Spring/Hibernate 等框架。
优点:
- 无需知道对象是什么,可以使用其内部相关的方法、属性等
- 提高代码的灵活度,可用于通用模块开发
- 提高扩展性,降低耦合性,提高自适应能力
缺点:
- 性能消耗大,慢于直接使用类、方法、属性等性能
- 增加逻辑难度,会带来维护困难