在 Java 中,反射(Reflection) 是一种允许程序在运行时检查、修改类/对象内部信息,并动态调用方法的机制。它的核心思想是通过运行时分析类结构,突破静态编译的限制,实现高度灵活的操作。
核心功能
-
运行时获取类信息
动态获取类的名称、父类、接口、字段(Field)、方法(Method)、构造方法(Constructor)等元数据。 -
动态操作对象
- 创建对象实例(即使构造函数是 private)
- 读写字段值(即使字段是私有)
- 调用方法(包括私有方法)
-
突破访问限制
通过setAccessible(true)
方法,可以绕过private
、protected
等访问修饰符的限制。
关键类和接口
类/接口 | 作用 |
---|---|
java.lang.Class | 表示类的元数据(如类名、方法、字段等) |
java.lang.reflect.Method | 表示类的方法,可用于动态调用 |
java.lang.reflect.Field | 表示类的字段,支持读取或修改值 |
java.lang.reflect.Constructor | 表示类的构造方法,用于创建对象 |
使用步骤示例
// 1. 获取类的 Class 对象 Class<?> clazz = Class.forName("com.example.User"); // 2. 创建实例(通过无参构造方法) Object user = clazz.newInstance(); // 3. 获取方法并调用 Method setNameMethod = clazz.getMethod("setName", String.class); setNameMethod.invoke(user, "Alice"); // 相当于调用 user.setName("Alice"); // 4. 访问私有字段并修改值 Field ageField = clazz.getDeclaredField("age"); ageField.setAccessible(true); // 绕过访问检查 ageField.set(user, 30); // 直接设置私有字段的值
核心应用场景
- 框架开发
如 Spring 框架通过反射实现依赖注入(@Autowired
)、动态代理(AOP)等。 - 单元测试
JUnit 通过反射找到带有@Test
注解的方法并执行。 - 序列化工具
Jackson/GSON 解析 JSON 时,通过反射获取类的字段信息。 - 动态扩展
插件化系统动态加载类(如通过Class.forName()
)。
反射的优缺点
优点
- 极大提高代码灵活性(如根据配置文件动态调用类)。
- 适合开发通用框架和工具。
缺点
- 性能开销大:反射操作比直接调用慢约 10~100 倍(JVM 优化受限)。
- 安全问题:可能破坏封装性,访问私有成员。
- 代码可读性差:反射代码较复杂,不易调试。
使用建议
- 谨慎使用:仅在需要动态性(如框架设计)时使用反射。
- 缓存结果:避免重复获取
Method
、Field
对象(通过Map
缓存)。 - 替代方案:优先考虑接口、设计模式(如工厂模式)等更安全的动态机制。
使用反射时,务必注意代码的性能和安全风险!