JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
一、反射的核心类
Java 反射的核心 API 位于 java.lang.reflect
包中,主要类包括:
-
Class<T>
:表示一个类或接口的元数据。 -
Constructor<T>
:表示类的构造方法。 -
Field
:表示类的成员变量(字段)。 -
Method
:表示类的方法。
二、获取 Class
对象的三种方式
- 通过类名获取
Class<?> clazz1 = String.class; // 直接通过类的静态属性获取
- 通过对象实例获取
String str = "Hello";
Class<?> clazz2 = str.getClass(); // 对象.getClass()
- 通过全限定类名动态加载
Class<?> clazz3 = Class.forName("java.lang.String"); // 需处理 ClassNotFoundException
因为在一个类在 JVM 中只会有一个 Class 实例,所以对 c1 、 c2 、 c3 进行 equals 比较时返回的都是
true 。
三、反射的核心功能
- 创建对象
// 获取无参构造方法并实例化
Class<?> clazz = Class.forName("com.example.User");
Constructor<?> constructor = clazz.getConstructor();
Object user = constructor.newInstance();
// 获取有参构造方法并实例化
Constructor<?> constructor = clazz.getConstructor(String.class, int.class);
Object user = constructor.newInstance("Alice", 25);
- 操作字段(包括私有字段)
// 获取字段并修改值
Field nameField = clazz.getDeclaredField("name");
nameField.setAccessible(true); // 暴力反射,突破私有权限
nameField.set(user, "Bob");
// 获取字段值
String name = (String) nameField.get(user);
- 调用方法(包括私有方法)
// 获取方法并调用
Method method = clazz.getDeclaredMethod("sayHello", String.class);
method.setAccessible(true); // 突破私有权限
Object result = method.invoke(user, "World"); // 调用实例方法
- 获取类信息
// 获取所有公共方法
Method[] methods = clazz.getMethods();
// 获取所有声明字段(包括私有)
Field[] fields = clazz.getDeclaredFields();
// 获取实现的接口
Class<?>[] interfaces = clazz.getInterfaces();
四、反射的优缺点
优点
-
动态性:运行时动态加载类、调用方法,适合框架和工具开发(如 Spring、Hibernate)。
-
灵活性:突破访问限制(如调用私有方法、修改私有字段)。
-
通用性:编写通用代码(如 JSON 序列化工具)。
缺点
-
性能开销:反射操作比直接调用慢(JVM 优化后差距缩小)。
-
安全性问题:破坏封装性,可能引发安全问题。
-
代码可读性差:反射代码通常难以维护。
五、反射的应用场景
-
框架开发
-
Spring 的依赖注入(IoC):通过反射实例化 Bean 并注入属性。
-
MyBatis 的 ORM 映射:动态设置查询结果到对象字段。
-
Spring 中的xml的配置模式
-
-
动态代理
- 结合
Proxy
和InvocationHandler
实现动态代理(如 AOP)。
- 结合
-
注解处理器
- 解析自定义注解并执行逻辑(如 JUnit 的
@Test
)。
- 解析自定义注解并执行逻辑(如 JUnit 的
-
通用工具类
-
序列化/反序列化工具(如 Jackson、Gson)。
-
数据库连接池的通用查询方法。
-
六、示例:通过反射调用私有方法
public class Demo {
private String secret() {
return "Confidential Data";
}
}
// 反射调用
public class ReflectionTest {
public static void main(String[] args) throws Exception {
Class<?> clazz = Class.forName("com.example.Demo");
Object obj = clazz.getDeclaredConstructor().newInstance();
Method method = clazz.getDeclaredMethod("secret");
method.setAccessible(true); // 突破私有权限
String result = (String) method.invoke(obj);
System.out.println(result); // 输出: Confidential Data
}
}
七、注意事项
-
性能优化:
-
缓存反射获取的
Method
、Field
对象,避免重复查找。 -
优先使用直接调用,反射仅用于必要场景。
-
-
安全管理:
- 通过
SecurityManager
限制反射权限(如禁止修改私有字段)。
- 通过
-
版本兼容性:
- Java 9+ 模块化系统可能限制反射访问,需在
module-info.java
中声明opens
。
- Java 9+ 模块化系统可能限制反射访问,需在
总结(ds学习记录)
Java 反射是强大的动态编程工具,但需谨慎使用。核心价值在于灵活性和动态性,适用于框架和通用工具开发,但在业务代码中应尽量避免滥用。