1. 反射机制的概念
反射(reflection)机制是指在程序的运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性。这种动态获取程序信息以及动态调用对象的功能称为 Java 语言的反射机制。
下面是 Java 代码在计算机中三个阶段:
- Source 源码阶段:.java 文件被编译为 .class 字节码文件。
- Class 类对象阶段:.class 字节码文件被类加载器加载进内存,并将其封装成 Class 类对象。Class 类对象会将字节码文件中的各个组成部分封装为对应的数组。(如:成员变量数组 Field[] fields,构造方法数组 Constructor[] cons)
- Runtime 运行时阶段:使用 new 创建对象。
2. 反射的用处
- 可以在程序运行过程中,操作对象。
- 可以解耦合,提高程序的可拓展性。
3. 反射的使用
3.1 获取 Class 对象
前面我们讲了"Class 类对象会将字节码文件中的各个组成部分封装为对应的数组",而我们要操作这些数组,首先就要获得类对象。
获得类对象的方式有三种:
-
Class.forName(“全类名”):将字节码文件加载进内存,返回 Class 对象。
(多用于配置文件,将类名定义在配置文件中。读取文件,加载类)
-
类名.class:通过类名的属性 class 获取。
(多用于参数的传递)
-
对象.getClass():getClass() 方法是在 Object 类中定义的。
(多用于对象的获取字节码的方式)
接下来验证上面的三个对象是否是同一对象
结果是同一对象,于是得出结论:
同一个字节码文件在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class对象都是同一个。
3.2 Class 对象的方法
- 获取成员变量
- Field[] getFields():获取所有 public 修饰的成员变量
- Field getField(String name):获取指定名称的 public 修饰的成员变量
- Field[] getDeclaredFields():获取所有的成员变量,不考虑修饰符
- Field getDeclaredField(String name):获取指定名称的成员变量,不考虑修饰符
- 获取构造方法
- Constructor<?>[] getConstructors()
- Constructor getConstructor(Class<?>… parameterTypes)
- Constructor<?>[] getDeclaredConstructors()
- Constructor getDeclaredConstructor(Class<?>… parameterTypes)
- 获取成员方法
- Method[] getMethods()
- Method getMethod(String name, Class<?>… parameterTypes)
- Method[] getDeclaredMethods()
- Method getDeclaredMethod(String name, Class<?>… parameterTypes)
- 获取全类名
- String getName()
3.3 操作对象
前面根据 Class 对象的方法已经可以获得类的各个组成部分了,下面用这些组成部分来操作具体的对象。
-
Field:成员变量
- 获取值:Object get(Object obj)
- 设置值:void set(Object obj, Object value)
- 忽略访问权限修饰符的安全检查:setAccessible(true)
-
Constructor:构造方法
-
创建对象:T newInstance(Object… initargs)
补充:如果使用空参数构造方法创建对象,可以直接用 Class 对象的 newInstance 方法创建
-
-
Method:方法对象
- 执行方法:Object invoke(Object obj, Object… args)
- 获取方法名称:String getName()
4. 反射的案例
-
需求分析
写一个“框架",在不改变该类的任何代码的前提下,可以帮我们创建任意类的对象,并且执行其中任意方法。
-
概要设计
先在配置文件中写好我们要创建对象的全类名和方法名,然后在程序中加载并读取配置文件,然后加载该类进内存,之后创建对象,再利用反射获取需要执行的方法,最后执行方法。
-
详细设计
- 将需要创建的对象的全类名和需要执行的方法定义在配置文件 pro.properties 中
- 在程序中加载配置文件到 properties 集合中
- 从 properties 集合中获取配置文件中定义的数据
- 根据全类名加载该类进内存,获取对应的 Class 对象
- 创建对象
- 利用反射获取需要执行的方法
- 执行该方法
-
编码实现
-
pro.properties(配置文件)
className=com.zt.domain.Person methodName=getName
-
ReflectTest
public class ReflectTest { public static void main(String[] args) throws Exception { //1. 加载配置文件到 properties 集合中 //1.1 创建 properties 对象,它相当于一个 Map 集合 Properties properties = new Properties(); //1.2 通过类加载器获取 class 目录下配置文件的路径 ClassLoader classLoader = ReflectTest.class.getClassLoader(); InputStream resourceAsStream = classLoader.getResourceAsStream("pro.properties"); //1.3 加载配置文件到 properties 集合中 properties.load(resourceAsStream); //2. 从 properties 集合中获取配置文件中定义的数据 String className = properties.getProperty("className"); String methodName = properties.getProperty("methodName"); //3. 根据全类名加载该类进内存,获取对应的 Class 对象 Class<?> aClass = Class.forName(className); //4. 创建对象 Object o = aClass.newInstance(); //5. 利用反射获取方法 Method method1 = aClass.getMethod(methodName); //6. 执行该方法 System.out.println(method1.invoke(o)); } }
-
执行结果
null Process finished with exit code 0
-