JAVA反射机制是在运行状态中,对于任意一个类,都能够得到这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。 简而言之,反射就是可以通过名称来得到对象(类,属性,方法)的技术。
一.获取类对应的Class对象
getClass()方式: Object类中的方法,每个类都拥有此方法,如: 1 2 String str = new String(); Class cls = str.getClass();
2.类名.class方式,如:
1 Class cls = String.class;
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 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(); }
调用有参的构造函数
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 的方式。