Java反射中私有字段需调用setAccessible(true)才能访问,但JDK12+在模块化等环境下可能被拒绝,需配合--add-opens;Method.invoke()要求基本类型装箱、可变参数需强转;Class.forName会初始化类而ClassLoader.loadClass不会;反射性能较低,应缓存Field/Method并慎用setAccessible(true),MethodHandle可提升性能。

Java反射中Field获取不到私有字段?
默认情况下,Field对象不会自动访问private成员,调用get()或set()会直接抛出IllegalAccessException。
- 必须显式调用
setAccessible(true)才能绕过Java语言访问检查 - 从JDK 12起,
setAccessible(true)在某些安全上下文(如模块化环境、启用--illegal-access=deny)下会被拒绝,需配合--add-opensJVM参数开放模块包,例如:--add-opens java.base/java.lang=ALL-UNNAMED - 注意:频繁调用
setAccessible(true)可能触发JVM的inflation机制,影响性能;可考虑缓存已设置过的Field对象
用Method.invoke()传参时类型不匹配?
Method.invoke()接收的是Object...参数,但原始类型(如int)必须装箱为对应包装类(如Integer),否则会抛出IllegalArgumentException。
- 基本类型参数不能直接传
int、boolean等,必须用Integer.valueOf(42)或自动装箱形式 - 若方法签名含可变参数(
String...),传入数组时要小心:传new String[]{"a","b"}会被当作单个String[]参数;想展开成两个String,得用(Object) new String[]{"a","b"}强转 - 静态方法调用时,第一个参数传
null即可;实例方法则必须传目标对象引用
Class.forName() vs ClassLoader.loadClass()区别在哪?
两者都能按字符串加载类,但行为关键差异在于「是否初始化类」:
-
Class.forName("com.example.Foo")会触发类的**初始化**(执行static块和静态字段赋值) -
ClassLoader.getSystemClassLoader().loadClass("com.example.Foo")只完成**加载和链接**,不初始化 - 若类中
static块有副作用(如注册驱动、初始化全局配置),用错方法会导致逻辑未执行或重复执行 - 多数框架(如Spring、MyBatis)内部优先使用
ClassLoader.loadClass()避免过早初始化
反射操作Field和Method的性能开销大不大?
反射本身比直接调用慢数倍到数十倍,尤其首次调用涉及解析、安全检查、适配器生成等;但现代JVM(JDK 9+)对热点反射路径做了优化,多次调用后差距缩小。
立即学习“Java免费学习笔记(深入)”;
- 避免在高频循环中反复
clazz.getDeclaredField("x")——应提前获取并缓存Field或Method对象 - 使用
MethodHandle(JDK 7+)替代Method.invoke()可显著提升性能,尤其适合需要极致速度的场景 - 注意:
setAccessible(true)本身也有开销,建议只在必要时设置,且不要对每个反射调用都重复设置
Field field = obj.getClass().getDeclaredField("value");
field.setAccessible(true); // 只设一次
Object val = field.get(obj); // 后续get无额外安全检查开销
反射真正难的不是语法,是搞清哪些检查能绕过、哪些绕不过,以及什么时候该用MethodHandle而不是硬扛invoke()。模块化、安全管理器、GraalVM原生镜像这些环境会让反射更“脆”,上线前务必在目标环境中实测。










