IllegalAccessException是JVM运行时拒绝反射访问私有成员的受检异常,因默认遵守访问控制规则而抛出;需调用setAccessible(true)绕过,但Java 9+受模块系统限制,可能失败或升级为InaccessibleObjectException。

Java中IllegalAccessException不是“反弹”,而是JVM在运行时明确拒绝反射访问私有成员的信号——它属于受检异常(checked exception),必须显式处理,但本质是权限控制机制的反馈,不是程序逻辑错误。
为什么调用私有属性会抛出这个异常
Java反射默认遵守访问控制规则:即使通过getDeclaredField()拿到了私有字段对象,field.get(obj)或field.set(obj, value)执行时仍会触发JVM的访问检查。未绕过检查就直接操作,就会抛IllegalAccessException。
- private字段本意就是禁止外部直接读写,反射不自动豁免
- 该异常与SecurityManager是否启用无关,是JVM内置的强制检查
- 它和
NoSuchFieldException不同——字段存在且能获取到,只是“不让碰”
setAccessible(true) 是标准解法,但要注意版本差异
绝大多数场景下,只需在反射操作前调用field.setAccessible(true)即可绕过检查。但Java 9+模块化后,行为有变化:
- JDK 8及之前:调用后基本总成功
- JDK 9–15:可能仍抛
IllegalAccessException,也可能升级为InaccessibleObjectException(后者是运行时异常) - JDK 16+默认启用
--illegal-access=deny,setAccessible(true)对跨模块私有成员直接失败
模块化环境下的真实限制不止于private修饰符
在JDK 9+中,光设setAccessible(true)不够,还受模块系统约束:
立即学习“Java免费学习笔记(深入)”;
-
exports只开放public类和成员,对private字段无效 - 必须用
opens声明包级开放,例如:opens com.example.model to spring.core; - 启动参数可临时缓解:
--add-opens java.base/java.lang=ALL-UNNAMED - 云函数、Android(API ≥ 28)、部分沙箱环境会彻底屏蔽该调用,无法靠代码修复
更稳妥的替代思路
依赖反射访问私有属性风险高、兼容性差,建议优先考虑以下方式:
- 为字段添加标准getter/setter,用公开API替代反射
- 使用
java.lang.invoke.MethodHandles.Lookup(仅限本模块内,JDK 12+更稳定) - 框架场景(如Jackson反序列化)升级到支持模块化的新版本,避免手动反射
- 测试中需mock私有逻辑,改用构造注入或接口抽象,而非硬反射调用


















