反射的概念和作用
Java 程序可以加载一个运行时才知道名称的类,得到它的完整构造,并可以生成实例对象、访问变量、或者调用其方法。
Java的反射机制主要提供了以下功能:
- 在运行时判断任意一个对象所属的类。
- 在运行时判断任意一个对象所具有的成员变量和方法。
- 在运行时构造任意一个类的对象。
- 在运行时调用任意一个对象的方法和变量。
- 生成动态代理(Dynamic Proxy)
反射机制大大增强了程序的灵活性,运行期进行代码转配的特性使我们得以构建灵活的代码。这点,在各种框架中得到了很好的展现。
Class 类型理解
开始理解 Class 类之前,我们先来看一下类在 JVM(Java Virtual Machine)层面怎么表示这个类。
每一个Java类(字节码文件),在被JVM加载的时候,JVM会给这个类创建一个instanceKlass,保存在方法区,用来在JVM层表示该Java类。当我们在Java代码中,使用new创建一个对象的时候,JVM会创建一个instanceOopDesc对象,这个对象中包含了对象头以及实例数据。
我们获取的 Class 对象即表示这个类类型 instanceKlass 对象。且 Class 没有公共构造方法。Class 对象是在加载类时由Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的,因此不能显式地声明一个Class对象。
Class 对象的三种获取方式
Java 提供了一套反射API来描述类,即 java.lang.reflect 包。
Construcor:描述类的构造方法。
Field:描述类的成员变量。
Method:描述类的方法。
Modifer:类内个元素的修饰符。
Array:用来对数组进行操作。
构造方法的获取
// 获得类的所有公共构造方法列表(public 修饰的构造方法)
Construcotr<?>[] getConstructors();
// 获得类的所有构造方法列表
Constructor<?>[] getDeclaredConstructors();
// 获得特定参数类型的公共构造函数(参数为可变长参数,不传参为无参构造)
Constructor<T> getConstructor(Class<?>... parameterTypes);
// 获得特定参数的构造函数
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes);
成员变量的获取
// 类的指定公共成员字段
Field getField(String name);
// 类的指定成员字段
Field getDeclaredField(String name);
// 获取所有公共成员字段
Field[] getFields();
// 获取所有成员字段
Field[] getDeclaredFields();
Field 类为变量提供了相应了 getter/setter 方法,以获取和修改变量的值。
成员方法的获取
// 获取指定公共成员方法
Method getMethod(String name, class<?>... parameterTypes);
// 获取指定成员方法
Method getDeclaredMethod(String name, class<?>... parameterTypes);
// 获取所有公共成员方法
Method[] getMethods();
// 获取所有成员方法
Method[] getDeclaredMethods();
暴力反射
如果类的成员方法、变量等被 private 修饰符修饰,默认情况下,外部无权访问。但是使用暴力反射,即使为类私有的属性或方法等,也可以进行操作。如下例所示:
实体类:
public class ReflectionTest {
public Integer id;
public String name;
private String password;
}
不使用暴力反射进行测试:
public static void main(String[] args) throws Exception {
Class klass = ReflectionTest.class;
ReflectionTest reflectionTest = (ReflectionTest) Class.forName(klass.getName()).newInstance();
Field password = klass.getDeclaredField("password");
System.out.println(password.get(reflectionTest));
}
测试结果:
非法访问:IllegalAccessException
使用暴力反射:
public static void main(String[] args) throws Exception {
Class klass = ReflectionTest.class;
ReflectionTest reflectionTest = (ReflectionTest) Class.forName(klass.getName()).newInstance();
Field password2 = klass.getDeclaredField("password");
password2.setAccessible(true);
System.out.println(password2.get(reflectionTest));
}
测试结果:
没有非法访问的异常。
反射的应用
动态代理
动态代理是反射的典型应用之一,如下段源码(java.lang.reflect.Proxy 中的 newProxyInstance 方法):
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
Class<?> cl = getProxyClass0(loader, intfs);
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
// 暴力破解
cons.setAccessible(true);
return null;
}
});
}
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}