反射原理
先看一段java反射使用的代码:
public class RefTest {
@Test
public void main() {
try {
// 加载class对象
Class clazz = Class.forName("cn.com.rabi.demotest.bean.Student");
// 获取所有公开构造函数
Constructor[] constructors = clazz.getConstructors();
for (Constructor constructor : constructors) {
System.out.println("1:"+constructor);
}
// 获取所有构造函数
constructors = clazz.getDeclaredConstructors();
for (Constructor constructor : constructors) {
System.out.println("2:"+constructor);
}
System.out.println("----------------------------------------------------------------");
// 调用protected构造
Constructor con = clazz.getDeclaredConstructor(String.class);
con.setAccessible(true);
Object o = con.newInstance("rabi");
// 调用private构造
con = clazz.getDeclaredConstructor(int.class);
con.setAccessible(true);
o = con.newInstance(11);
// 调用公有无参构造
con = clazz.getConstructor(null);
o = con.newInstance();
System.out.println("----------------------------------------------------------------");
// 获取所有公有字段
Field[] fields = clazz.getFields();
for (Field field : fields) {
System.out.println("3:"+field);
}
// 获取所有字段
fields = clazz.getDeclaredFields();
for (Field field : fields) {
System.out.println("4:"+field);
}
System.out.println("----------------------------------------------------------------");
// 获取公有字段并赋值
Field field = clazz.getField("name");
field.set(o,"mavis");
// 获取私有字段并赋值
field = clazz.getDeclaredField("age");
field.setAccessible(true);
field.set(o,11);
// 输出当前状态
Student student = (Student) o;
System.out.println(student.getName()+":"+student.getAge());
System.out.println("----------------------------------------------------------------");
// 获取所有公有方法
Method[] methods = clazz.getMethods();
for (Method method : methods) {
System.out.println("5:"+method);
}
// 获取所有方法
methods = clazz.getDeclaredMethods();
for (Method method : methods) {
System.out.println("6:"+method);
}
System.out.println("----------------------------------------------------------------");
// 获取公有方法调用
Method method = clazz.getMethod("setName", String.class);
method.invoke(o,"rabi");
System.out.println(student.getName()+":"+student.getAge());
// 获取私有方法并调用
method = clazz.getDeclaredMethod("setAge", int.class);
method.setAccessible(true);
method.invoke(o,17);
System.out.println(student.getName()+":"+student.getAge());
// 调用protected方法
method = clazz.getDeclaredMethod("getInfo");
method.setAccessible(true);
method.invoke(o);
System.out.println("----------------------------------------------------------------");
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException | NoSuchFieldException e) {
e.printStackTrace();
}
}
}
// 新建一个Student类
public class Student {
public String name;
private int age;
public Student() {
System.out.println("Student()");
}
private Student(int age) {
this.age = age;
System.out.println("Student(int age)");
}
protected Student(String name) {
this.name = name;
System.out.println("Student(int name)");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
private void setAge(int age) {
this.age = age;
}
protected void getInfo() {
System.out.println(name+":"+age);
}
}
上面这段代码的测试结果如下:
1:public cn.com.rabi.demotest.bean.Student()
2:public cn.com.rabi.demotest.bean.Student()
2:private cn.com.rabi.demotest.bean.Student(int)
2:protected cn.com.rabi.demotest.bean.Student(java.lang.String)
----------------------------------------------------------------
Student(int name)
Student(int age)
Student()
----------------------------------------------------------------
3:public java.lang.String cn.com.rabi.demotest.bean.Student.name
4:public java.lang.String cn.com.rabi.demotest.bean.Student.name
4:private int cn.com.rabi.demotest.bean.Student.age
----------------------------------------------------------------
mavis:11
----------------------------------------------------------------
5:public int cn.com.rabi.demotest.bean.Student.getAge()
5:public java.lang.String cn.com.rabi.demotest.bean.Student.getName()
5:public void cn.com.rabi.demotest.bean.Student.setName(java.lang.String)
5:public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
5:public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
5:public final void java.lang.Object.wait() throws java.lang.InterruptedException
5:public boolean java.lang.Object.equals(java.lang.Object)
5:public java.lang.String java.lang.Object.toString()
5:public native int java.lang.Object.hashCode()
5:public final native java.lang.Class java.lang.Object.getClass()
5:public final native void java.lang.Object.notify()
5:public final native void java.lang.Object.notifyAll()
6:private void cn.com.rabi.demotest.bean.Student.setAge(int)
6:protected void cn.com.rabi.demotest.bean.Student.getInfo()
6:public int cn.com.rabi.demotest.bean.Student.getAge()
6:public java.lang.String cn.com.rabi.demotest.bean.Student.getName()
6:public void cn.com.rabi.demotest.bean.Student.setName(java.lang.String)
----------------------------------------------------------------
rabi:11
rabi:17
rabi:17
----------------------------------------------------------------
这里我们取其中调用调用公有的空构造方法,然后调用公有的setName方法进行分析。
首先是获取class,查看源码:
public static Class> forName(String className)
throws ClassNotFoundException {
Class> caller = Reflection.getCallerClass();
return forName(className, true, ClassLoader.getClassLoader(caller));
}
public static Class> forName(String name, boolean initialize,
ClassLoader loader)
throws ClassNotFoundException
{
if (loader == null) {
loader = BootClassLoader.getInstance();
}
Class> result;
try {
result = classForName(name, initialize, loader);
} catch (ClassNotFoundException e) {
Throwable cause = e.getCause();
if (cause instanceof LinkageError) {
throw (LinkageError) cause;
}
throw e;
}
return result;
}
可以看到这里是调用的ClassLoader获取了class对象并返回,然后获取公有无参构造函数,查看源码:
public Constructor getConstructor(Class>... parameterTypes)
throws NoSuchMethodException, SecurityException {
return getConstructor0(parameterTypes, Member.PUBLIC);
}
private Constructor getConstructor0(Class>[] parameterTypes,
int which) throws NoSuchMethodException
{
// 这里我们传入的参数是null
if (parameterTypes == null) {
parameterTypes = EmptyArray.CLASS;
}
for (Class> c : parameterTypes) {
if (c == null) {
throw new NoSuchMethodException("parameter type is null");
}
}
Constructor result = getDeclaredConstructorInternal(parameterTypes);
if (result == null || which == Member.PUBLIC && !Modifier.isPublic(result.getAccessFlags())) {
throw new NoSuchMethodException(getName() + ". "
+ Arrays.toString(parameterTypes));
}
return result;
}
这里的getDeclaredConstructorInternal是native方法,返回Constructor对象,查看其newInstance方法:
public T newInstance(Object ... initargs)
throws InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException
{
if (serializationClass == null) {
return newInstance0(initargs);
} else {
return (T) newInstanceFromSerialization(serializationCtor, serializationClass);
}
}
这里也是调用的native方法,返回了T对象,其中serializationClass!=null的情况会有一次类型转换。接着往下看,获取公有方法的getMethod函数和获取所有方法的getDeclaredMethods函数:
@CallerSensitive
public Method getMethod(String name, Class>... parameterTypes)
throws NoSuchMethodException, SecurityException {
return getMethod(name, parameterTypes, true);
}
public Method getDeclaredMethod(String name, Class>... parameterTypes)
throws NoSuchMethodException, SecurityException {
return getMethod(name, parameterTypes, false);
}
private Method getMethod(String name, Class>[] parameterTypes, boolean recursivePublicMethods)
throws NoSuchMethodException {
// 方法名不能为空
if (name == null) {
throw new NullPointerException("name == null");
}
// 如果没有参数
if (parameterTypes == null) {
parameterTypes = EmptyArray.CLASS;
}
// 检查参数对象中是否有null
for (Class> c : parameterTypes) {
if (c == null) {
throw new NoSuchMethodException("parameter type is null");
}
}
// 获取公有方法的时候这里为true
// getPublicMethodRecursive是一个native方法,最终返回一个method对象
Method result = recursivePublicMethods ? getPublicMethodRecursive(name, parameterTypes)
: getDeclaredMethodInternal(name, parameterTypes);
// Fail if we didn't find the method or it was expected to be public.
// 检查public
if (result == null ||
(recursivePublicMethods && !Modifier.isPublic(result.getAccessFlags()))) {
throw new NoSuchMethodException(getName() + "." + name + " "
+ Arrays.toString(parameterTypes));
}
return result;
}
private Method getPublicMethodRecursive(String name, Class>[] parameterTypes) {
// 遍历继承类
for (Class> c = this; c != null; c = c.getSuperclass()) {
// 找到该方法
Method result = c.getDeclaredMethodInternal(name, parameterTypes);
// 判断该方法是否是公有的
if (result != null && Modifier.isPublic(result.getAccessFlags())) {
return result;
}
}
return findInterfaceMethod(name, parameterTypes);
}
可以看到两个方法的区别就是传入getMethod的第三个参数true/false,之后返回一个Method对象,查看invoke方法:
public native Object invoke(Object obj, Object... args)
throws IllegalAccessException, IllegalArgumentException, InvocationTargetException;
这里返回的也是一个Object对象。感觉jdk8和jdk12在这部分的差异挺大的,具体相信部分可以参考末尾的《大家都说 Java 反射效率低,你知道原因在哪里么》一文。
分析源码之后,我们这里也回答一下为什么反射效率低:
1.校验参数,当传递参数类的时候,需要校验参数类型。
2.方法查找,无论是公有还是私有,都是遍历的操作。
3.类型转换,参数以class>传入的,返回值是object的时候需要转换为T。
感觉意犹未尽,待续。
参考文献