illegalaccessexception是jvm因访问权限检查失败而拒绝反射访问私有/包私有成员的信号;java 9+模块化下setaccessible(true)可能抛inaccessibleobjectexception,因模块未opens/exports对应包或--illegal-access=deny启用。

IllegalAccessException 是反射调用时被拦下的信号
它不是代码写错了,而是 JVM 明确拒绝了你用 Reflection 去碰某个成员(比如私有字段、包私有方法)。根本原因就一个:访问权限检查没过,且你没主动绕过它。
为什么 setAccessible(true) 不总管用
在模块化(Java 9+)环境下,setAccessible(true) 可能直接抛 InaccessibleObjectException,而不是 IllegalAccessException。这时候不是反射权限问题,是模块导出/开放限制——JVM 根本不让你“看到”那个类或成员。
- 模块未用
opens或exports声明开放对应包(例如opens com.example.internal to java.base;) - 目标类在 unnamed module(比如传统 classpath 启动),但调用方在 named module,跨模块访问默认被拒
- 使用
--illegal-access=deny(Java 16+ 默认)会彻底禁用旧式反射绕过
常见触发场景和对应解法
最常踩坑的是反序列化框架(如 Jackson、Gson)、测试工具(Mockito)、ORM(Hibernate)依赖反射访问私有字段,结果在 JDK 11+ 上突然失败。
- 测试中 mock 私有方法?改用
Mockito.mockConstruction()或构造器注入,别硬反射调private方法 - 想读取
final static字段?先field.setAccessible(true),再用Unsafe或VarHandle(IllegalAccessException拦不住后者,但更重) - Spring Boot 应用启动报这个异常?检查是否用了老版本 starter(如 spring-boot-starter-data-jpa
兼容 JDK 8 到 JDK 21 的务实做法
不要指望一套反射逻辑通吃所有 JDK。真要跨版本支持,得按运行时 JDK 版本分叉处理:
立即学习“Java免费学习笔记(深入)”;
- JDK ≤ 8:直接
field.setAccessible(true); field.get(obj) - JDK 9–15:加 try-catch
IllegalAccessException和InaccessibleObjectException,提示用户加 JVM 参数(如--add-opens java.base/java.lang=ALL-UNNAMED) - JDK 16+:放弃反射私有成员,改用 public API(如
java.lang.invoke.MethodHandles.Lookup构造可访问句柄,但仅限本模块内)
模块边界比访问修饰符更硬,这点很多人一开始没意识到。










