反射机制
编译阶段不知道创建哪个类的时候可以使用反射机制,在运行过程中创建。例如SpringBean工厂,在运行时创建各个bean就利用了反射机制。先看一段代码。
class Student {
private String name = "默认值";
public Student() {
System.out.println("调用无参构造器");
}
public Student(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
'}';
}
}
public class TestReflect {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException, NoSuchMethodException, InvocationTargetException {
Class<?> c = Class.forName("com.wyb.csdnlearning.base.Student");
// 实例化
Object obj = c.newInstance();
// 获取并填充属性
Field name = c.getDeclaredField("name");
// 打破封装(可以访问私有)
name.setAccessible(true);
name.set(obj, "路人甲");
// 获取和调用方法
Method setName = c.getDeclaredMethod("setName", String.class);
setName.invoke(obj, "路人乙");
Method toString = c.getDeclaredMethod("toString");
System.out.println(toString.getDeclaredAnnotations().toString());
System.out.println(toString.invoke(obj));
}
}
这里面有三大关键类,第一个是Class类,第二个是Field类,第三个是Method类,着三种类分别对应:类名、属性、方法。为了更好的了解,我们从源码分析。
上图是Class类的类结构图,可以看到主要实现了Type、GenericDeclaration和AnnotatedElement三个接口。其中Type比较简单,只有一个默认方法,获取类型名(全程),但是Class也重写了它,作用是如果是数组类型,根据维度再添加一些细节(如果是二维,那就后面加两个"[][]")。GenericDeclaration也只有一个方法——getTypeParameters,该方法用于获取类、方法、构造方法的泛型。AnnotatedElement接口定义获取注解相关信息的方法。现在重点关注一下Class类的newInstance()方法。
// Class类源码
public T newInstance()
throws InstantiationException, IllegalAccessException
{
if (System.getSecurityManager() != null) {
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), false);
}
// NOTE: the following code may not be strictly correct under
// the current Java memory model.
// Constructor lookup
if (cachedConstructor == null) {
// 不允许使用Class来创建Class类
if (this == Class.class) {
throw new IllegalAccessException(
"Can not call newInstance() on the Class for java.lang.Class"
);
}
try {
Class<?>[] empty = {};
// 获取构造器,这里用的是无参构造器(empty = {})
// 后面的Member.DECLARED忽略继承的
final Constructor<T> c = getConstructor0(empty, Member.DECLARED);
// Disable accessibility checks on the constructor
// since we have to do the security check here anyway
// (the stack depth is wrong for the Constructor's
// security check to work)
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Void>() {
public Void run() {
c.setAccessible(true);
return null;
}
});
// 把构造器保存起来,以后再new的时候不用再加载了
cachedConstructor = c;
} catch (NoSuchMethodException e) {
throw (InstantiationException)
new InstantiationException(getName()).initCause(e);
}
}
Constructor<T> tmpConstructor = cachedConstructor;
// Security check (same as in java.lang.reflect.Constructor)
int modifiers = tmpConstructor.getModifiers();
if (!Reflection.quickCheckMemberAccess(this, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
if (newInstanceCallerCache != caller) {
Reflection.ensureMemberAccess(caller, this, null, modifiers);
newInstanceCallerCache = caller;
}
}
// Run constructor
try {
// 这里开始new,但实际上这个过程还是很复杂
// 使用Constructor的方法
// 调用DelegatingConstructorAccessorImpl实体类的方法
// 然后到NativeConstructorAccessorImpl
// 最终会到无参构造器中
return tmpConstructor.newInstance((Object[])null);
} catch (InvocationTargetException e) {
Unsafe.getUnsafe().throwException(e.getTargetException());
// Not reached
return null;
}
}
// 获取属性方法
public Field getDeclaredField(String name)
throws NoSuchFieldException, SecurityException {
checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
// 这个做法是获取所有的Fields列表,然后一个个对比名字能不能对的上
Field field = searchFields(privateGetDeclaredFields(false), name);
if (field == null) {
throw new NoSuchFieldException(name);
}
return field;
}
在通过Class获取到Field之后,Field提供了一系列的getter和setter方法。同样,Method类是目标类的方法。有一点需要注意的是,无论是用Field的get和set方法还是Method的invoke方法,都需要传入具体实例对象,因为在获取Field或Method的时候,获取的是Class的而非Instance的。