Java 反射现代实践速查表(JDK 11+/17+)
适用人群:想快速上手/查阅反射写法的 Java 工程师。 运行环境:建议 JDK 11+,最佳 JDK 17/21。 关键词:Class、Field、Method、Constructor、MethodHandle、VarHandle、Proxy、Type。
目录
-
- 反射是什么
-
- 获取
Class的三种方式
- 获取
-
- 现代实例化对象(替代
Class.newInstance())
- 现代实例化对象(替代
-
- 访问字段
Field
- 访问字段
-
- 调用方法
Method
- 调用方法
-
- 构造器
Constructor
- 构造器
-
- 注解与参数信息
-
- 泛型反射:
Type家族
- 泛型反射:
-
- JDK 9+ 模块化与反射访问(
--add-opens/opens) - 动态代理
Proxy - MethodHandle / VarHandle(高性能反射)
- 性能优化与缓存
- 安全要点与限制
- 常见异常与排错速览
- 实用片段(可直接复用)
- 最佳实践清单
- 延伸阅读
- JDK 9+ 模块化与反射访问(
1. 反射是什么
- 运行时检查类型结构(类/字段/方法/构造器/注解),并可读写字段、调用方法、创建对象。
- 常用于:依赖注入、ORM、序列化/反序列化、AOP、测试工具、插件系统等。
2. 获取 Class 的三种方式
Class> c1 = String.class; // 字面量
Class> c2 = "Hello".getClass(); // 实例 -> Class
Class> c3 = Class.forName("java.lang.String"); // 全限定名加载
小贴士:基本类型与数组也有 Class:int.class、String[].class。
3. 现代实例化对象(替代 Class.newInstance())
不要再用clazz.newInstance()(JDK 9+ 已弃用)。 推荐使用无参构造器的标准替代:
MyType obj = MyType.class.getDeclaredConstructor().newInstance();
- 有参构造:
Constructor ctor = MyType.class.getDeclaredConstructor(String.class, int.class);
ctor.setAccessible(true); // 若非 public
MyType obj = ctor.newInstance("Ocean", 42);
说明:getDeclaredConstructor 能拿到非 public 构造;遇到访问限制需结合模块化策略(见 §9)。
4. 访问字段 Field
Field f = MyType.class.getDeclaredField("name"); // 含私有
f.setAccessible(true); // 破私有(受模块限制)
f.set(obj, "Ocean");
Object val = f.get(obj);
- 列出所有声明字段(不含父类):
for (Field fd : MyType.class.getDeclaredFields()) {
fd.setAccessible(true);
System.out.println(fd.getName() + " : " + fd.getType());
}
5. 调用方法 Method
Method m = MyType.class.getDeclaredMethod("sayHello", String.class);
m.setAccessible(true);
Object ret = m.invoke(obj, "World");
- 列出 public 方法(含继承):
for (Method pm : MyType.class.getMethods()) {
System.out.println(pm);
}
6. 构造器 Constructor
- 获取并调用:
Constructor ctor = MyType.class.getDeclaredConstructor();
ctor.setAccessible(true);
MyType instance = ctor.newInstance();
- 列出所有声明构造器:
for (Constructor> c : MyType.class.getDeclaredConstructors()) {
System.out.println(c);
}
7. 注解与参数信息
// 类、字段、方法上的注解
MyAnno anno = MyType.class.getAnnotation(MyAnno.class);
// 方法参数名(需编译参数 -parameters)
Method m = MyType.class.getDeclaredMethod("foo", String.class, int.class);
for (Parameter p : m.getParameters()) {
System.out.println(p.getName() + " : " + p.getType());
}
8. 泛型反射:Type 家族
Field f = Repo.class.getDeclaredField("data"); // e.g. Map>
Type t = f.getGenericType();
if (t instanceof ParameterizedType pt) {
Type raw = pt.getRawType(); // Map
Type[] args = pt.getActualTypeArguments(); // [String, List]
}
- 常见
Type子接口:Class>:普通类型ParameterizedType:带泛型参数的类型,如ListTypeVariable:类型变量,如TWildcardType:通配符,如? extends NumberGenericArrayType:泛型数组,如T[]
9. JDK 9+ 模块化与反射访问(--add-opens/opens)
- 现象:对非导出包的私有成员做反射(哪怕
setAccessible(true)),会抛IllegalAccessException或非法反射访问警告/失败。 - 解决:
- 运行时打开包(适合应用层调试/临时兼容)
java --add-opens my.module/com.example.internal=ALL-UNNAMED -jar app.jar
2. **模块描述符中显式开放**(长期方案)
// module-info.java
open module my.module {
// 或者有选择地 opens 某些包
// opens com.example.internal to other.module;
}
- 注意:JDK 17+ 对 JDK 内部 API 强封装更严格;尽量依赖标准 API,不要反射 JDK 内部包。
10. 动态代理 Proxy
- 在运行期为接口生成代理对象,常用于 AOP、拦截器、RPC Stub。
interface Hello { String hi(String name); }
InvocationHandler h = (proxy, method, args) -> {
if (method.getName().equals("hi")) {
return "[LOG] " + args[0];
}
return method.invoke(proxy, args);
};
Hello hello = (Hello) Proxy.newProxyInstance(
Hello.class.getClassLoader(),
new Class>[]{Hello.class},
h
);
System.out.println(hello.hi("Ocean")); // [LOG] Ocean
如果需要代理类(非接口),可使用 ByteBuddy、CGLIB、Javassist 等字节码库。
11. MethodHandle / VarHandle(高性能反射)
**MethodHandle**:更接近直接调用的可链接句柄,类型安全,常比Method.invoke更快。
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle mh = lookup.findVirtual(MyType.class, "sayHello",
MethodType.methodType(String.class, String.class));
String r = (String) mh.invoke(obj, "World");
**VarHandle**(JDK 9+):字段/数组/字节缓冲区的“变量句柄”,支持原子/内存栅栏等操作:
VarHandle NAME;
static {
try {
NAME = MethodHandles.lookup()
.in(MyType.class)
.findVarHandle(MyType.class, "name", String.class);
} catch (Exception e) { throw new RuntimeException(e); }
}
NAME.set(obj, "Ocean");
String v = (String) NAME.get(obj);
一般规则:频繁调用/性能敏感 → 优先考虑 MethodHandle/VarHandle;一次性工具/低频 → 传统反射 API 足够。
12. 性能优化与缓存
- 缓存反射元数据:
Field/Method/Constructor查找与setAccessible代价不小,建议按类型做缓存。 - 避免热路径反射:把反射调用移出循环,或转换为
MethodHandle并缓存句柄。 - JIT 热身:基准测试前做预热,避免冷启动干扰。
13. 安全要点与限制
- 反射破坏封装,需谨慎对外暴露;对不可信输入(类名/成员名)务必校验白名单。
- 在受限环境(安全管理器已废弃但仍有沙箱/容器策略)可能禁止反射访问。
- 对JDK 内部包的访问在现代 JDK 中被强封装,不要依赖非标准内部 API。
14. 常见异常与排错速览
ClassNotFoundException:类名或类路径错误。NoSuchMethodException/NoSuchFieldException:签名或名称不匹配(包含参数类型/顺序)。IllegalAccessException:访问权限/模块未opens。InvocationTargetException:被调用方法抛出的异常被包装在此处。InaccessibleObjectException(JDK 16+ 常见):模块强封装导致;用--add-opens或opens解决。
15. 实用片段(可直接复用)
15.1 安全获取无参构造实例(现代写法)
public static T newInstance(Class type) {
try {
Constructor c = type.getDeclaredConstructor();
c.setAccessible(true);
return c.newInstance();
} catch (ReflectiveOperationException e) {
throw new IllegalStateException("Cannot instantiate: " + type.getName(), e);
}
}
15.2 读/写私有字段(带模块检查提示)
public static Object readField(Object target, String name) {
try {
Field f = target.getClass().getDeclaredField(name);
f.setAccessible(true); // 若报 InaccessibleObjectException,参见 §9
return f.get(target);
} catch (ReflectiveOperationException e) {
throw new IllegalStateException(e);
}
}
15.3 遍历声明方法(含注解)
for (Method m : clazz.getDeclaredMethods()) {
m.setAccessible(true);
System.out.println(m.getName());
for (Annotation a : m.getAnnotations()) {
System.out.println(" @" + a.annotationType().getSimpleName());
}
}
15.4 解析字段的泛型参数
public static List> genericArgsOfField(Field f) {
Type t = f.getGenericType();
if (t instanceof ParameterizedType pt) {
List> list = new ArrayList<>();
for (Type arg : pt.getActualTypeArguments()) {
if (arg instanceof Class> cls) list.add(cls);
// 可扩展处理 WildcardType/TypeVariable 等
}
return list;
}
return List.of();
}
15.5 缓存 MethodHandle
final class Handles {
private static final Map CACHE = new ConcurrentHashMap<>();
static MethodHandle vh(Class> c, String name, Class> ret, Class>... params) {
return CACHE.computeIfAbsent(c.getName() + "#" + name, k -> {
try {
return MethodHandles.lookup().findVirtual(c, name, MethodType.methodType(ret, params));
} catch (Throwable e) { throw new RuntimeException(e); }
});
}
}
16. 最佳实践清单
- 实例化:
getDeclaredConstructor().newInstance()取代Class.newInstance()。 - 模块化:跨模块反射请使用
--add-opens或在module-info.java中opens。 - 性能:高频反射改用
MethodHandle/VarHandle,并缓存句柄/成员。 - 健壮性:明确捕获
ReflectiveOperationException分支,封装为业务异常。 - 安全:对外暴露的“类名/成员名”必须白名单校验;不要反射 JDK 内部包。
- 可维护性:优先接口与多态,反射只用于必要环节。
17. 延伸阅读
- Oracle 教程:The Reflection API(系统讲解与注意事项) docs.oracle.com/javase/tuto…
- Dev.java 教程(较新的官方教学站点) dev.java/learn/refle…
- Java 平台模块系统(JEP 261):openjdk.org/jeps/261
- 强封装 JDK 内部(JEP 403):openjdk.org/jeps/403