啥是 Java 的反射机制(reflection)? Java
反射机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取信息和动态调用对象方法的功能称为 Java
的反射机制。
那为啥要整个 Java 的反射机制呢? 因为在第三方应用的开发过程中,经常会遇到某个类的方法或者属性是私有的或者只对系统开放,这时候无法直接调用,就可通过 Java
的反射机制来获取这些方法或者变量。
反射看起来破坏了封装性,可以让私有变量被外部访问到,类就不那么安全了。 Oracle
的官方文档中将发射的主要作用如下:
反射让开发人员可以通过外部类的全路径名创建对象,并使用这些类,以实现一些扩展功能。
反射让开发人员可以枚举类的全部成员,包括构造函数、属性和方法等,以帮助开发人员写出正确的代码。
测试时可以利用反射 API
访问类的私有变量,以保证测试代码的覆盖率。
简言之,反射设计的初衷是作为开发者的一个工具,是用来帮助程序员实现一些本来不可能实现的功能的。如果没有反射,对于外部类的私有成员,我们将一筹莫展,所以我们有了反射这一后门,为程序设计提供了更大的灵活性。工具本身并没有错,关键在于如何正确地使用。
反射 API 反射 API 种类与功能 Java 类的成员包括三类:属性字段、构造函数和方法。而反射的 API 也与这些相关。如下所示
创建 demo 新建一个包,创建一个类。其中包含了需要测试的所有情况,私有属性、公有属性、公有构造方法、私有构造方法、公有方法和私有方法。
Student.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package com.example.reflection;public class Student { private String studentName; public int studentAge; public Student () { } private Student (String studentName) { this .studentName = studentName; } public void setStudentAge (int studentAge) { this .studentAge = studentAge; } private String show (String message) { System.out.println("show: " + studentName + "," + studentAge + "," + message); return "testReturnValue" ; } }
API 使用对象 以下都以 com.example.reflection.Student
作为蓝本对象。
获取 class 对象 获取 class
有以下三种方式。
1 2 3 4 5 6 7 8 9 Class studentClass = Class.forName("com.example.reflection.Student" );Class studentClass2 = Student.class;Student studentObject = new Student ();Class studentClass3 = studentObject.getClass();
第一种方法是通过类的全路径完整包名去获取 class
对象。这也是平时最常用的反射获取 class
对象的方法。
第二种方法有限制,需要能带入类的包。
第三种方法已经有了现成的 class
的对象了,不再需要反射来获取了。
进一步观察,我们可以将上述三种方法的结果进行打印测试一下他们是否是同一个对象。
Main.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import com.example.reflection.Student;public class Main { public static void main (String[] args) throws Exception{ Class studentClass = Class.forName("com.example.reflection.Student" ); Class studentClass2 = Student.class; Student studentObject = new Student (); Class studentClass3 = studentObject.getClass(); System.out.println("class1 = " + studentClass + "\n" + "class2 = " + studentClass2 + "\n" + "class3 = " + studentClass3 + "\n" + "class1 == class2 ? " + (studentClass == studentClass2) + "\n" + "class2 == class3 ? " + (studentClass2 == studentClass3)); } }
运行结果如下:
1 2 3 4 5 6 PS D:\Users\Desktop\work> java Main class1 = class com.example.reflection.Student class2 = class com.example.reflection.Student class3 = class com.example.reflection.Student class1 == class2 ? true class2 == class3 ? true
通过上述三种方法获取到的对象是同一个,也就是说 Java
在运行时,每个类指挥生成一个 class
对象。
获取到 class
对象之后就可以该怎么操作就怎么操作了。
获取成员变量 获取字段有两个 API
: getDeclaredFields
和 getFields
。前者用于获取所有声明的字段,包括公有的字段和私有的字段;后者仅仅用了获取公有字段。
示例代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import java.lang.reflect.Field;System.out.println("\nall Fields declared:" ); Field[] declaredFieldList = studentClass.getDeclaredFields(); for (Field declaredField : declaredFieldList) { System.out.println("declared Field: " + declaredField); } System.out.println("\nall public Fields declared:" ); Field[] fieldList = studentClass.getFields(); for (Field field : fieldList) { System.out.println("field: " + field); }
运行结果如下所示:
1 2 3 4 5 6 7 8 PS D:\Users\Desktop\work> java Main all Fields declared: declared Field: private java.lang.String com.example.reflection.Student.studentName declared Field: public int com.example.reflection.Student.studentAge all public Fields declared: field: public int com.example.reflection.Student.studentAge
获取构造方法 获取构造方法同样包含两个 API
:用于获取所有构造方法的 getDeclaredConstructors
和用于获取公有构造方法的 getConstructors
。
示例代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import java.lang.reflect.Constructor;System.out.println("\nall Constructors declared:" ); Constructor[] declaredConstructorList = studentClass.getDeclaredConstructors(); for (Constructor declaredConstructor : declaredConstructorList) { System.out.println("declared Constructor: " + declaredConstructor); } System.out.println("\nall public Constructors declared:" ); Constructor[] constructorList = studentClass.getConstructors(); for (Constructor constructor : constructorList) { System.out.println("constructor: " + constructor); }
运行结果如下所示:
1 2 3 4 5 6 7 8 PS D:\Users\Desktop\work> java Main all Constructors declared: declared Constructor: public com.example.reflection.Student() declared Constructor: private com.example.reflection.Student(java.lang.String) all public Constructors declared: constructor: public com.example.reflection.Student()
获取非构造方法 获取非构造方法的两个 API
是:获取所有声明的非构造函数的 getDeclaredMethods
和仅获取公有非构造函数的 getMethods
。
示例代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import java.lang.reflect.Method;System.out.println("\nall Methods declared:" ); Method[] declaredMethodList = studentClass.getDeclaredMethods(); for (Method declaredMethod : declaredMethodList) { System.out.println("declared Method: " + declaredMethod); } System.out.println("\nall public Methods declared:" ); Method[] methodList = studentClass.getMethods(); for (Method method : methodList) { System.out.println("method: " + method); }
输出结果如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 PS D:\Users\Desktop\work> java Main all Methods declared: declared Method: private java.lang.String com.example.reflection.Student.show(java.lang.String) declared Method: public void com.example.reflection.Student.setStudentAge(int) all public Methods declared: method: public void com.example.reflection.Student.setStudentAge(int) method: public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException method: public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException method: public final void java.lang.Object.wait() throws java.lang.InterruptedException method: public boolean java.lang.Object.equals(java.lang.Object) method: public java.lang.String java.lang.Object.toString() method: public native int java.lang.Object.hashCode() method: public final native java.lang.Class java.lang.Object.getClass() method: public final native void java.lang.Object.notify() method: public final native void java.lang.Object.notifyAll()
这里可以看到 getMethods
不仅获取到了自定义的公有方法,还可以获取到父类( Object
)的公有方法。但是 getDeclaredMethods
无法获取到父类中的方法。
上述四种获取方法完整程序:
Main.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 import com.example.reflection.Student;import java.lang.reflect.Field;import java.lang.reflect.Constructor;import java.lang.reflect.Method;public class Main { public static void main (String[] args) throws Exception{ Class studentClass = Class.forName("com.example.reflection.Student" ); Class studentClass2 = Student.class; Student studentObject = new Student (); Class studentClass3 = studentObject.getClass(); System.out.println("class1 = " + studentClass + "\n" + "class2 = " + studentClass2 + "\n" + "class3 = " + studentClass3 + "\n" + "class1 == class2 ? " + (studentClass == studentClass2) + "\n" + "class2 == class3 ? " + (studentClass2 == studentClass3)); System.out.println("\nall Fields declared:" ); Field[] declaredFieldList = studentClass.getDeclaredFields(); for (Field declaredField : declaredFieldList) { System.out.println("declared Field: " + declaredField); } System.out.println("\nall public Fields declared:" ); Field[] fieldList = studentClass.getFields(); for (Field field : fieldList) { System.out.println("field: " + field); } System.out.println("\nall Constructors declared:" ); Constructor[] declaredConstructorList = studentClass.getDeclaredConstructors(); for (Constructor declaredConstructor : declaredConstructorList) { System.out.println("declared Constructor: " + declaredConstructor); } System.out.println("\nall public Constructors declared:" ); Constructor[] constructorList = studentClass.getConstructors(); for (Constructor constructor : constructorList) { System.out.println("constructor: " + constructor); } System.out.println("\nall Methods declared:" ); Method[] declaredMethodList = studentClass.getDeclaredMethods(); for (Method declaredMethod : declaredMethodList) { System.out.println("declared Method: " + declaredMethod); } System.out.println("\nall public Methods declared:" ); Method[] methodList = studentClass.getMethods(); for (Method method : methodList) { System.out.println("method: " + method); } } }
完整实现反射调用 直接贴代码如下,建议先自己手打再对比。
Main.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 import java.lang.reflect.Field;import java.lang.reflect.Constructor;import java.lang.reflect.Method;public class Main { public static void main (String[] args) throws Exception { Class studentClass = Class.forName("com.example.reflection.Student" ); Constructor studentConstructor = studentClass.getDeclaredConstructor(String.class); studentConstructor.setAccessible(true ); Object student = studentConstructor.newInstance("studentNameA" ); Field studentAgeField = studentClass.getDeclaredField("studentAge" ); studentAgeField.set(student, 100 ); Method studentShowMethod = studentClass.getDeclaredMethod("show" , String.class); studentShowMethod.setAccessible(true ); Object result = studentShowMethod.invoke(student, "message" ); System.out.println("result: " + result); } }
运行程序的输出结果如下所示,第二行输出是 Student.show()
的调用产生的。第三行则是 Main.java
打印的调用的返回结果。
1 2 3 PS D:\Users\Desktop\work> java Main show: studentNameA,100,message result: testReturnValue
若要查询更多方法或者 API
则可以查看参考链接一或者官方文档。
参考链接 参考链接一:(主要写了各种接口方法等) https://www.jianshu.com/p/9be58ee20dee
参考链接二: https://zhuanlan.zhihu.com/p/86293659