Java反射

  JAVA反射机制是在运行状态中,对于任意一个类,都能够得到这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
  简而言之,反射就是可以通过名称来得到对象(类,属性,方法)的技术。

一.获取类对应的Class对象

  1. getClass()方式: Object类中的方法,每个类都拥有此方法,如:
    1
    2
    String str = new String();
    Class cls = str.getClass();

2.类名.class方式,如:

1
Class cls = String.class;
  1. Class.forName()方式,如:
    1
    2
    3
    4
    5
    6
    try {
    Class cls = Class.forName("java.lang.String");
    } catch (ClassNotFoundException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }

二.构造一个类的实例

  1. 调用无参的构造函数:

    1
    2
    3
    4
    5
    6
    7
    Class cls = Student.class;
    try {
    Student stu = (Student) cls.newInstance();
    } catch (InstantiationException | IllegalAccessException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }
  2. 调用有参的构造函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    try {
    Class cls = Class.forName("com.zero.Student");
    //指定参数的Class,来决定调用那个构造函数
    Constructor cons = cls.getConstructor(String.class, int.class);
    Student stu = (Student) cons.newInstance("zero", 10);
    } catch (InstantiationException | IllegalAccessException
    | ClassNotFoundException | NoSuchMethodException
    | SecurityException | IllegalArgumentException | InvocationTargetException e) {
    e.printStackTrace();
    }

三.对象的属性操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Class cls = Student.class;// 首先得到class
Field[] fields = cls.getFields(); // 只能获取public修饰的field
for (Field field : fields) {
System.out.println(field.getName());
}

fields = cls.getDeclaredFields(); // 获取所有声明过的field
for (Field field : fields) {
System.out.println(field.getName());
}

Student stu = new Student("zero", 12);
Field stuFiled = stu.getClass().getDeclaredField("name"); // 获取private的field
stuFiled.setAccessible(true);// private访问权限打开,否则抛出java.lang.IllegalAccessException
System.out.println(stuFiled.get(stu));// 获取stu的name

Student student = Student.class.newInstance();
Field sFiled = Student.class.getDeclaredField("name");
sFiled.setAccessible(true);
sFiled.set(student, "zero123");// 为name字段设置值
System.out.println(sFiled.get(student));

四.类方法操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
  Method[] methods = Student.class.getMethods();
for (Method method : methods) { // 只获取Student的public修饰的method,以及Object的public修饰的method
System.out.println(method.getName());
}

Method[] methodss = Student.class.getDeclaredMethods();
for (Method method : methodss) { // 只获取Student所有的method,不包括Object的method
System.out.println(method.getName());
}

try {
Student student = new Student();
Method setMethod = student.getClass().getDeclaredMethod(
"setGender", String.class);//获取参数为String的setGender方法
setMethod.invoke(student,"man");

Method getMethod = student.getClass().getDeclaredMethod(
"getGender");//获取getGender方法
getMethod.setAccessible(true);//getGender方法是private,应该打开访问权限
String gender = (String) getMethod.invoke(student);
System.out.println(gender);
} catch (Exception e) {
e.printStackTrace();
}

  来剖析一下Method#invoke()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@CallerSensitive
public Object invoke(Object obj, Object... args)
throws IllegalAccessException, IllegalArgumentException,
InvocationTargetException
{
if (!override) {
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
checkAccess(caller, clazz, obj, modifiers);
}
}
MethodAccessor ma = methodAccessor; // read volatile
if (ma == null) {
ma = acquireMethodAccessor();
}
return ma.invoke(obj, args);
}

  可以最终调用了MethodAccessor.invoke方法,MethodAccessor 是一个接口:

1
2
3
public interface MethodAccessor {
Object invoke(Object var1, Object[] var2) throws IllegalArgumentException, InvocationTargetException;
}

  调试代码可以发现看到 MethodAccessor 的实现类是委托类DelegatingMethodAccessorImpl,它的 invoke 函数非常简单,就是把调用委托给了真正的实现类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class DelegatingMethodAccessorImpl extends MethodAccessorImpl {
private MethodAccessorImpl delegate;

DelegatingMethodAccessorImpl(MethodAccessorImpl var1) {
this.setDelegate(var1);
}

public Object invoke(Object var1, Object[] var2) throws IllegalArgumentException, InvocationTargetException {
return this.delegate.invoke(var1, var2);
}

void setDelegate(MethodAccessorImpl var1) {
this.delegate = var1;
}
}

  DelegatingMethodAccessorImpl中的delegate即NativeMethodAccessorImpl,真正的调用是NativeMethodAccessorImpl:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class NativeMethodAccessorImpl extends MethodAccessorImpl {
private final Method method;
private DelegatingMethodAccessorImpl parent;
private int numInvocations;

NativeMethodAccessorImpl(Method var1) {
this.method = var1;
}

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);
}

  NativeMethodAccessorImpl中会用numInvocations记录方法调用的次数,当次数小于ReflectionFactory.inflationThreshold(默认15)次时,直接调用invoke0方法,则是一个 native 的函数。
  15 次以后会走新的逻辑,使用 MethodAccessorGenerator(ASM的方式)生成一个新类,并将该类设置为DelegatingMethodAccessorImpl的delegate,这样下次直接调用的是新类的方法。

  为什么要采用 0 ~ 15 次使用 native 方式来调用,15 次以后使用 ASM 新生成的类来处理反射的调用呢?这是因为JNI native 调用的方式要比动态生成类调用的方式慢 20 倍,但是又由于第一次字节码生成的过程比较慢。如果反射仅调用一次的话,采用生成字节码的方式反而比 native 调用的方式慢 3 ~ 4 倍。
  很多情况下,反射只会调用一次,设置了sun.reflect.inflationThreshold 这个阈值,反射方法调用超过这个阈值时,采用 ASM 生成新的类,保证后面的调用比 native 要快。如果小于 15 次的情况下,还不如生成直接 native 来的简单直接,还不造成额外类的生成、校验、加载。这种方式被称为 inflation 机制。
  JVM 与 inflation 相关的属性有两个,一个是sun.reflect.inflationThreshold,还有一个是否禁用 inflation的属性 sun.reflect.noInflation,默认值为 false。如果把这个值设置成true 的话,从第 0 次开始就使用动态生成类的方式来调用反射方法了,不会使用 native 的方式。