下面是一篇基于最新 JDK 实践,内容涵盖:概念 → 基本 API → JDK 9+ 变化 → 安全与性能 → 最佳实践,并附示例代码。
✅ Java 反射现代实践指南(JDK 11+ / 17+ 适用)
1. 什么是反射?
反射(Reflection) 是 Java 提供的一种机制,允许程序在运行时动态地:
- 检查类的结构(字段、方法、构造器、注解等)
- 访问和修改对象属性
- 调用方法
- 创建实例
它打破了编译期的静态类型限制,使框架(如 Spring、MyBatis)能够实现依赖注入、ORM 映射等功能。
2. 反射的核心 API
反射主要位于 java.lang.reflect
包,核心类包括:
Class<T>
:类型元信息入口Field
:字段Method
:方法Constructor<T>
:构造器
2.1 获取 Class
对象
perl
Class<?> clazz1 = String.class;
Class<?> clazz2 = "Hello".getClass();
Class<?> clazz3 = Class.forName("java.lang.String");
2.2 创建实例(现代写法)
不要再用
Class.newInstance()
,它在 JDK 9 起已弃用。
✅ 推荐:
ini
MyClass obj = MyClass.class.getDeclaredConstructor().newInstance();
原因:
- 支持异常细分(
ReflectiveOperationException
) - 与模块化访问控制兼容
2.3 访问字段
csharp
Field f = MyClass.class.getDeclaredField("name");
f.setAccessible(true); // 破私有(需注意模块限制)
f.set(obj, "Ocean");
System.out.println(f.get(obj));
2.4 调用方法
ini
Method m = MyClass.class.getDeclaredMethod("sayHello", String.class);
m.setAccessible(true);
String result = (String) m.invoke(obj, "World");
System.out.println(result);
3. JDK 9+ 模块化带来的变化
3.1 强封装(JEP 261 & JEP 403)
- JDK 9 引入模块系统,非导出包的反射访问会报 IllegalAccessException。
- JDK 17 起(JEP 403),JDK 内部 API 强封装,不能随意反射访问。
3.2 如何解决?
- 启动参数添加
--add-opens
:
csharp
java --add-opens java.base/java.lang=ALL-UNNAMED ...
- 或在
module-info.java
中显式opens
。
4. 性能与安全
-
性能开销:反射调用比直接调用慢 10~20 倍(JIT 优化后仍有差距)。
-
安全风险 :
setAccessible(true)
绕过封装,可能破坏模块边界。 -
最佳实践:
- 避免在高频路径使用反射。
- 可用
MethodHandle
(Java 7+)或VarHandle
(Java 9+)替代,性能更好。
5. 现代最佳实践清单
✅ 实例化
scss
clazz.getDeclaredConstructor().newInstance();
✅ 跨模块访问
- 使用
--add-opens
或module-info.java
的opens
。
✅ 性能敏感场景
- 优先
MethodHandle
:
ini
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle mh = lookup.findVirtual(MyClass.class, "sayHello",
MethodType.methodType(String.class, String.class));
String result = (String) mh.invoke(obj, "World");
✅ 避免滥用反射
- 能用接口/多态解决的,不要用反射。
6. 实战示例:动态调用方法
arduino
public class ReflectionDemo {
public static void main(String[] args) throws Exception {
Class<?> clazz = Class.forName("com.example.MyClass");
Object obj = clazz.getDeclaredConstructor().newInstance();
Method m = clazz.getDeclaredMethod("sayHello", String.class);
m.setAccessible(true);
String result = (String) m.invoke(obj, "Ocean");
System.out.println(result);
}
}