Java 反射相关整理

1. Class 类

  • Class 是一个类,封装了当前对象所对应的类的信息,一个类中有属性,方法,构造器等。
    • 对于每个类而言,JRE 都为其保留一个不变的 Class 类型的对象。一个 Class 对象包含了特定某个类的有关信息。
    • Class 对象只能由系统建立对象,一个类(而不是一个对象)在 Java 虚拟机中只会有一个 Class 实例。
    • Class 对象的由来是将 class 文件读入内存,并为之创建一个 Class 对象。

获取 Class 类对象的三种方法

  • 使用 Class.forName 静态方法,当知道类的全路径名时,可以使用该方法获取 Class 类对象。
Class clz = Class.forName("java.lang.String");
  • 使用 .class 方法,这种方法适合在编译前就知道操作的 Class。
Class clz = String.class;
  • 使用类对象的 getClass() 方法。
String str = new String("Hello");
Class clz = str.getClass();

Class 类的常用方法

方法名功能说明
static Class forName(String name) 返回指定类名 name 的 Class 对象。
Object newInstance() 调用缺省构造函数,返回该 Class 对象的一个实例。
getName() 返回此 Class 对象所表示的实体(类、接口、数组类、基本类型或 void)名称。
Class getSuperClass() 返回当前 Class 对象的父类 Class 对象。
Class [] getInterfaces() 获取当前 Class 对象的接口。
ClassLoader getClassLoader() 返回该类的类加载器。

2. Java 反射(Reflection)

  • Java 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性,这种动态获取的信息以及动态调用对象的方法的功能称为 Java 语言的反射机制。
    • 反射是 Java 被视为动态语言的关键,反射机制允许程序在执行期借助于 Reflection API 取得任何类的內部信息,并能直接操作任意对象的内部属性及方法。
    • 反射机制就是可以把一个类、类的成员(函数,属性),当成一个对象来操作。
    • Java 反射主要提供以下功能。
      • 在运行时判断任意一个对象所属的类。
      • 在运行时构造任意一个类的对象。
      • 在运行时判断任意一个类所具有的成员变量和方法(通过反射甚至可以调用 private 方法)。
      • 在运行时调用任意一个对象的方法。
      • 生成动态代理。

2.1 通过反射访问构造函数

  • 为了能够动态获取对象构造方法的信息,需要创建一个 Constructor 类型的对象或者数组。
    • getConstructors()
    • getConstructor(Class<?>…parameterTypes)
    • getDeclaredConstructors()
    • getDeclaredConstructor(Class<?>...parameterTypes)
  • 创建的每个 Constructor 对象表示一个构造方法,然后利用 Constructor 对象的方法操作构造方法。
Constructor 对象方法名称说明
isVarArgs() 查看该构造方法是否允许带可变数量的参数,如果允许,返回 true,否则返回 false。
getParameterTypes() 按照声明顺序以 Class 数组的形式获取该构造方法各个参数的类型。
getExceptionTypes() 以 Class 数组的形式获取该构造方法可能抛出的异常类型。
newInstance(Object … initargs) 通过该构造方法利用指定参数创建一个该类型的对象,如果未设置参数则表示采用默认无参的构造方法。
setAccessiable(boolean flag) 如果该构造方法的权限为 private,默认为不允许通过反射利用 netlnstance() 方法创建对象。如果先执行该方法,并将入口参数设置为 true,则允许创建对象。
getModifiers() 获得可以解析出该构造方法所采用修饰符的整数。
public void testConstructor() throws Exception{
        String className = "com.java.test.Person";
        Class<Person> clazz = (Class<Person>) Class.forName(className);
        
        // 获取全部 Constructor 对象
        Constructor<Person> [] constructors = (Constructor<Person>[]) Class.forName(className).getConstructors();
        
        // 获取某一个,需要参数列表
        Constructor<Person> constructor = clazz.getConstructor(String.class, int.class);
        
        // 调用构造器的 newInstance() 方法创建对象
        Object obj = constructor.newInstance("test", 1);                
}
Modifier 对象的静态方法名称说明
isStatic(int mod) 如果使用 static 修饰符修饰则返回 true,否则返回 false。
isPublic(int mod) 如果使用 public 修饰符修饰则返回 true,否则返回 false。
isProtected(int mod) 如果使用 protected 修饰符修饰则返回 true,否则返回 false。
isPrivate(int mod) 如果使用 private 修饰符修饰则返回 true,否则返回 false。
isFinal(int mod) 如果使用 final 修饰符修饰则返回 true,否则返回 false。
toString(int mod) 以字符串形式返回所有修饰符。
int modifiers = con.getModifiers();    //获取构造方法的修饰符整数。
boolean isPubiic = Modifier.isPublic(modifiers);    //判断修饰符整数是否为 public。
public string ailModifSers = Modifier.toString(modifiers);

2.2 通过反射执行方法(获取方法)

  • 要动态获取一个对象方法的信息,需要创建一个 Method 类型的对象或者数组。

    • getMethods()
    • getMethods(String name,Class<?> …parameterTypes)
    • getDeclaredMethods()
    • getDeclaredMethods(String name,Class<?>...parameterTypes)
  • 如果是访问指定的构造方法,需要根据该方法的入口参数的类型来访问。

objectCiass.getDeclaredConstructor("max",int.class,String.class);
objectClass.getDeclaredConstructor("max",new Ciass[]{int.class,String.class});
 
Method 对象静态方法名称说明
getName() 获取该方法的名称。
getParameterType() 按照声明顺序以 Class 数组的形式返回该方法各个参数的类型。
getRetumType() 以 Class 对象的形式获得该方法的返回值类型。
getExceptionTypes() 以 Class 数组的形式获得该方法可能抛出的异常类型。
invoke(Object obj,Object...args) 利用 args 参数执行指定对象 obj 中的该方法,返回值为 Object 类型。
isVarArgs() 查看该方法是否允许带有可变数量的参数,如果允许返回 true,否 则返回 false。
getModifiers() 获得可以解析出该方法所采用修饰符的整数。
public void testMethod() throws Exception{
        Class clazz = Class.forName("com.java.test.Person");
        
        // 获取取 clazz 对应类中的所有方法,但不能获取 private 方法,且获取从父类继承来的所有方法。
        Method[] methods = clazz.getMethods();
        
        // 获取所有方法,包括私有方法,获取所有声明的方法,且只获取当前类的方法
        methods = clazz.getDeclaredMethods();
       
        // 获取指定的方法,需要参数名称和参数列表,无参则不需要
        //  方法 public void setName(String name)
        Method method = clazz.getDeclaredMethod("setName", String.class);

        //  方法 public void setAge(int age)
        //  方法用于反射,只能对应 int.class,不能对应 Integer.class
        method = clazz.getDeclaredMethod("setAge", int.class);
        
        // 执行方法
        //  invoke 第一个参数表示执行某个对象的方法,剩下的参数是执行方法时需要传入的参数
        // 私有方法的执行,必须在调用 invoke 之前加上 method.setAccessible(true);
        Object obje = clazz.newInstance();
        method.invoke(obje,2);
}

2.3 通过反射访问成员变量

  • 通过方法访问成员变量时将返回 Field 类型的对象或数组。

    • getFields()
    • getField(String name)
    • getDeclaredFields()
    • getDeclaredField(String name)
  • 返回的 Field 对象代表一个成员变量。

object.getDeciaredField("price");
 
Field 的方法名称说明
getName() 获得该成员变量的名称。
getType() 获取表示该成员变量的 Class 对象。
get(Object obj) 获得指定对象 obj 中成员变量的值,返回值为 Object 类型。
set(Object obj,Object value) 将指定对象 obj 中成员变量的值设置为 value。
getlnt(0bject obj) 获得指定对象 obj 中成员类型为 int 的成员变量的值。
setlnt(0bject obj,int i) 将指定对象 obj 中成员变量的值设置为 i。
setFloat(Object obj,float f) 将指定对象 obj 中成员变量的值设置为 f。
getBoolean(Object obj) 获得指定对象 obj 中成员类型为 boolean 的成员变量的值。
setBoolean(Object obj,boolean b) 将指定对象 obj 中成员变量的值设置为 b。
getFloat(Object obj) 获得指定对象 obj 中成员类型为 float 的成员变量的值。
setAccessible(boolean flag) 此方法可以设置是否忽略权限直接访问 private 等私有权限的成员变量。
getModifiers() 获得可以解析出该方法所采用修饰符的整数。
public void testField() throws Exception{
        String className = "com.java.test.Person";        
        Class clazz = Class.forName(className); 
        
        // 获取所有字段,可以获取公用和私有的所有字段,但不能获取父类字段
        Field[] fields = clazz.getDeclaredFields();
        
        // 获取指定字段
        Field field = clazz.getDeclaredField("name");
        
        Person person = new Person("ABC",12);
        
        //获取指定对象的指定字段的值
        Object val = field.get(person);
        
        //设置指定对象的指定对象 Field 值
        field.set(person, "DEF");
        
        // 如果字段是私有的,不管是读值还是写值,都必须先调用 setAccessible(true) 方法
        field.setAccessible(true);
        field = clazz.getDeclaredField("age");
        
}

2.4 通过反射访问注解

  • 定义一个注解。
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(value={ElementType.METHOD})
public @interface AgeValidator {
    public int min();
    public int max();
}
  • 应用于方法上。
@AgeValidator(min=18,max=35)
public void setAge(int age) {
        this.age = age;
}
  • 通过反射的方式为属性赋值,获取注解。
public void testAnnotation() throws Exception{
        String className = "com.java.test.Person";
        
        Class clazz = Class.forName(className);
        Object obj = clazz.newInstance();    
        
        Method method = clazz.getDeclaredMethod("setAge", int.class);
        int val = 6;
        
        //获取指定名称的注解
        Annotation annotation = method.getAnnotation(AgeValidator.class);
        if(annotation != null){
            if(annotation instanceof AgeValidator){
                AgeValidator ageValidator = (AgeValidator) annotation;                
                if(val < ageValidator.min() || val > ageValidator.max()){
                    throw new RuntimeException("年龄非法");
                }
            }
        }        
        method.invoke(obj, 20);
}
  • 如果在程序中需要获取注解,然后根据获取注解的值进而判断赋值是否合法,那么类对象的创建和方法的创建必须通过反射而来。

3. 实现原理

3.1 Method 的获取

  • 通过调用 Class 类的 getDeclaredMethod() 方法可以获取指定方法名和参数的方法对象 Method。
  • 调用 checkMemberAccess() 方法进行一些权限检查。
@CallerSensitive
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
        throws NoSuchMethodException, SecurityException {
        checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
        Method method = searchMethods(privateGetDeclaredMethods(false), name, parameterTypes);
        if (method == null) {
            throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes));
        }
        return method;
}
  • 调用 privateGetDeclaredMethods() 方法从缓存或 Java 虚拟机获取 Class 中申明的方法列表。
  • 调用 searchMethods() 方法从返回的方法列表里找到一个匹配名称和参数的方法对象(Method)。
private static Method searchMethods(Method[] methods,
                                        String name,
                                        Class<?>[] parameterTypes)
{
        Method res = null;
        String internedName = name.intern();
        for (int i = 0; i < methods.length; i++) {
            Method m = methods[i];
            if (m.getName() == internedName
                && arrayContentsEq(parameterTypes, m.getParameterTypes())
                && (res == null
                    || res.getReturnType().isAssignableFrom(m.getReturnType())))
                res = m;
        }

        return (res == null ? res : getReflectionFactory().copyMethod(res));
}
  • 如果找到一个匹配的方法对象(Method),则重新拷贝一份返回。
    • 实际调用了 Method.copy() 方法。
Method copy() {
        // This routine enables sharing of MethodAccessor objects
        // among Method objects which refer to the same underlying
        // method in the VM. (All of this contortion is only necessary
        // because of the "accessibility" bit in AccessibleObject,
        // which implicitly requires that new java.lang.reflect
        // objects be fabricated for each reflective call on Class
        // objects.)
        if (this.root != null)
            throw new IllegalArgumentException("Can not copy a non-root Method");

        Method res = new Method(clazz, name, parameterTypes, returnType,
                                exceptionTypes, modifiers, slot, signature,
                                annotations, parameterAnnotations, annotationDefault);
        res.root = this;
        // Might as well eagerly propagate this if already present
        res.methodAccessor = methodAccessor;
        return res;
}
  • 因为每次调用 getDeclaredMethod() 方法返回的方法对象(Method)都是一个新的对象,且新对象的 root 属性都指向原来的 Method 对象,如果频繁的调用,则最好的方式就是将 Method 缓存起来。

privateGetDeclaredMethods 的缓存实现

  • privateGetDeclaredMethods() 方法会从缓存或 Java 虚拟机获取 Class 中申明的方法列表。
private Method[] privateGetDeclaredMethods(boolean publicOnly) {
        checkInitted();
        Method[] res;
        ReflectionData<T> rd = reflectionData();
        if (rd != null) {
            res = publicOnly ? rd.declaredPublicMethods : rd.declaredMethods;
            if (res != null) return res;
        }
        // No cached value available; request value from VM
        res = Reflection.filterMethods(this, getDeclaredMethods0(publicOnly));
        if (rd != null) {
            if (publicOnly) {
                rd.declaredPublicMethods = res;
            } else {
                rd.declaredMethods = res;
            }
        }
        return res;
}
  • 延迟创建和缓存 ReflectionData 数据对象。
private ReflectionData<T> reflectionData() {
        SoftReference<ReflectionData<T>> reflectionData = this.reflectionData;
        int classRedefinedCount = this.classRedefinedCount;
        ReflectionData<T> rd;
        if (useCaches &&
            reflectionData != null &&
            (rd = reflectionData.get()) != null &&
            rd.redefinedCount == classRedefinedCount) {
            return rd;
        }
        // else no SoftReference or cleared SoftReference or stale ReflectionData
        // -> create and replace new instance
        return newReflectionData(reflectionData, classRedefinedCount);
}
  • ReflectionData 数据对象,用来缓存从 Java 虚拟机中读取类的一些属性数据。
private static class ReflectionData<T> {
        volatile Field[] declaredFields;
        volatile Field[] publicFields;
        volatile Method[] declaredMethods;
        volatile Method[] publicMethods;
        volatile Constructor<T>[] declaredConstructors;
        volatile Constructor<T>[] publicConstructors;
        // Intermediate results for getFields and getMethods
        volatile Field[] declaredPublicFields;
        volatile Method[] declaredPublicMethods;
        volatile Class<?>[] interfaces;

        // Value of classRedefinedCount when we created this ReflectionData instance
        final int redefinedCount;

        ReflectionData(int redefinedCount) {
            this.redefinedCount = redefinedCount;
        }
}
  • ReflectionData 数据对象实现为 SoftReference 类型,在内存紧张时可能会被回收,也可以通过 -XX:SoftRefLRUPolicyMSPerMB 参数控制回收时机,发生 GC 就将其回收。
  • 如果 ReflectionData 数据对象被回收后又执行了反射方法,那只能通过 newReflectionData() 方法重新创建一个新的 ReflectionData 对象。
private ReflectionData<T> newReflectionData(SoftReference<ReflectionData<T>> oldReflectionData,
                                                int classRedefinedCount) {
        if (!useCaches) return null;

        while (true) {
            ReflectionData<T> rd = new ReflectionData<>(classRedefinedCount);
            // try to CAS it...
            if (Atomic.casReflectionData(this, oldReflectionData, new SoftReference<>(rd))) {
                return rd;
            }
            // else retry
            oldReflectionData = this.reflectionData;
            classRedefinedCount = this.classRedefinedCount;
            if (oldReflectionData != null &&
                (rd = oldReflectionData.get()) != null &&
                rd.redefinedCount == classRedefinedCount) {
                return rd;
            }
        }
}
  • 通过 unsafe.compareAndSwapObject() 方法重新设置 reflectionData 字段。
reflectionDataOffset = objectFieldOffset(fields, "reflectionData");
......
static <T> boolean casReflectionData(Class<?> clazz,
                                             SoftReference<ReflectionData<T>> oldData,
                                             SoftReference<ReflectionData<T>> newData) {
            return unsafe.compareAndSwapObject(clazz, reflectionDataOffset, oldData, newData);
}
  • privateGetDeclaredMethods() 方法中如果通过 reflectionData() 方法获得的 ReflectionData 数据对象不为空,则尝试从 ReflectionData 数据对象中获取 declaredMethods 属性。
if (rd != null) {
      res = publicOnly ? rd.declaredPublicMethods : rd.declaredMethods;
      if (res != null) return res;
}
  • 如果是第一次创建 ReflectionData 数据对象或者 ReflectionData 对象已被 GC 回收过,重新初始化后的类属性为空,则需要重新到 Java 虚拟机中获取一次,并赋值给 ReflectionData,便于下次调用使用缓存数据。
// No cached value available; request value from VM
res = Reflection.filterMethods(this, getDeclaredMethods0(publicOnly));
if (rd != null) {
        if (publicOnly) {
             rd.declaredPublicMethods = res;
         } else {
             rd.declaredMethods = res;
         }
}

3.2 Method 的调用

  • 获取到指定的方法对象(Method)后,调用执行 invoke() 方法。
  • 调用 quickCheckMemberAccess() 方法进行一些权限检查。
@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() 方法。acquireMethodAccessor() 方法可以生成 MethodAccessor 对象。
private MethodAccessor acquireMethodAccessor() {
        // First check to see if one has been created yet, and take it
        // if so
        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);
        }

        return tmp;
}
  • 判断是否存在对应的 MethodAccessor 对象,如果存在则复用,否则调用 ReflectionFactory 对象的 newMethodAccessor() 方法生成一个 MethodAccessor 对象。
    • 其中 noInflation 为关闭 Inflation 机制,可通过 -Dsun.reflect.noInflation=true 设置。
public MethodAccessor newMethodAccessor(Method var1) {
        checkInitted();
        if (noInflation && !ReflectUtil.isVMAnonymousClass(var1.getDeclaringClass())) {
            return (new MethodAccessorGenerator()).generateMethod(var1.getDeclaringClass(), var1.getName(), var1.getParameterTypes(), var1.getReturnType(), var1.getExceptionTypes(), var1.getModifiers());
        } else {
            NativeMethodAccessorImpl var2 = new NativeMethodAccessorImpl(var1);
            DelegatingMethodAccessorImpl var3 = new DelegatingMethodAccessorImpl(var2);
            var2.setParent(var3);
            return var3;
        }
}
  • 如果使用了 Inflation 机制(noInflation = false),那么这里的实现使用了代理模式,将 NativeMethodAccessorImpl 对象交给 DelegatingMethodAccessorImpl 对象代理。
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;
    }
}
  • 那么 MethodAccessor 调用的 invoke() 方法,实际调用了 DelegatingMethodAccessorImpl 对象的 invoke() 方法,最终调用了被代理的 NativeMethodAccessorImpl 对象的 invoke() 方法。
    • NativeMethodAccessorImpl 对象的 invoke() 方法中会判断调用次数是否超过阀值(numInvocations)。
    • 如果超过该阀值,会生成另外一个 MethodAccessor 对象,并将原来 DelegatingMethodAccessorImpl 对象中的 delegate 属性指向最新的 MethodAccessor 对象。
    • numInvocations 默认为 15,可以通过 -Dsun.reflect.inflationThreshold= 设置次数。
private static int inflationThreshold = 15;
......
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);
}
  • 实际的 MethodAccessor 接口实现有两个版本,一个是 Native 版本,一个是 Java 版本。
    • Native 版本的实现是 NativeMethodAccessorImpl 对象。
    • Java 版本的实现是 MethodAccessorImpl。

Inflation 机制

  • 初次加载字节码实现反射,使用 Method.invoke()Constructor.newInstance() 加载花费的时间是使用原生代码加载花费时间的 3 - 4 倍,这使得那些频繁使用反射的应用需要花费更长的启动时间。
  • 为了避免这种痛苦的加载时间,第一次加载的时候重用了 Java 虚拟机的入口,之后再切换到字节码实现的实现。

  • Native 版本一开始启动很快,随着运行时间变长,速度变慢。
  • Java 版本一开始加载慢,随着运行时间变长,速度变快。
  • 所以当第一次加载时会发现使用的是 NativeMethodAccessorImpl 对象的实现,当反射调用次数超过 15(numInvocations) 次之后,则使用 MethodAccessorGenerator 生成的 MethodAccessorImpl 对象去实现反射。
    • generateMethod() 方法在生成 MethodAccessorImpl 对象时,会在内存中生成对应的字节码,并调用 ClassDefiner.defineClass() 创建对应的 class 对象。
return (MagicAccessorImpl)AccessController.doPrivileged(new PrivilegedAction<MagicAccessorImpl>() {
                public MagicAccessorImpl run() {
                    try {
                        return (MagicAccessorImpl)ClassDefiner.defineClass(var13, var17, 0, var17.length, var1.getClassLoader()).newInstance();
                    } catch (IllegalAccessException | InstantiationException var2) {
                        throw new InternalError(var2);
                    }
                }
});
  • ClassDefiner.defineClass() 方法实现中,每被调用一次都会生成一个 DelegatingClassLoader 类加载器对象。
    • 每次生成新的类加载器是为了性能考虑(从设计来看,本身不希望这些类一直存在于内存当中,在需要的时候存在即可)。
      • 在某些情况下可以直接卸载这些生成的类,因为类的卸载是只有在类加载器可以被回收的情况下才会被回收的,如果使用原来的类加载器,可能导致这些新创建的类一直无法被卸载。
static Class<?> defineClass(String var0, byte[] var1, int var2, int var3, final ClassLoader var4) {
        ClassLoader var5 = (ClassLoader)AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
            public ClassLoader run() {
                return new DelegatingClassLoader(var4);
            }
        });
        return unsafe.defineClass(var0, var1, var2, var3, var5, (ProtectionDomain)null);
}
  • invoke() 方法的内部使用了代理的设计模式来实现最大化性能。

参考资料

https://www.cnblogs.com/chanshuyi/p/head_first_of_reflection.html
http://www.importnew.com/23902.html
http://www.importnew.com/23560.html
http://www.importnew.com/21211.html



原文地址:https://www.cnblogs.com/youngao/p/12576512.html