IllegalAccessError本质是模块系统在类链接阶段强制拦截跨模块非法引用,而非运行时反射权限问题;错误因模块未导出包导致,典型提示为“module X does not export Y to module Z”,发生在字节码解析期,早于InaccessibleObjectException。

IllegalAccessError 不是运行时权限问题,而是模块系统在类加载阶段就拒绝跨模块访问非导出包的硬性拦截。它比 IllegalAccessException 和 InaccessibleObjectException 更早触发,说明问题出在“模块可见性”而非“反射可访问性”层面。
IllegalAccessError 的本质:模块边界在链接期就被卡死
这个错误发生在 JVM 类链接(linking)阶段,不是执行反射时抛出的——这意味着哪怕你没调 setAccessible(true),只要字节码里出现了对另一个模块非导出包内类的直接引用(比如 extends、implements、static field access),JVM 就会立刻报错。
- 典型场景:某第三方库继承了 jdk.internal.misc.Unsafe,或静态引用了 sun.nio.ch.FileChannelImpl
- 关键特征:错误信息中明确写出 “because module X does not export Y to module Z”
- 和反射异常的区别:InaccessibleObjectException 是反射调用时检查失败;IllegalAccessError 是类加载/链接时就拒绝组装字节码引用
从错误堆栈反推模块关系的三步定位法
遇到 IllegalAccessError,别急着加参数,先看清楚谁想碰谁:
- 找“cannot access class A in module B” → A 是被访问类,B 是它所属模块(如 java.base、jdk.unsupported)
- 找“because module B does not export A’s package to module C” → C 是你的模块名,若显示 unnamed module,说明你用的是传统 jar 启动
- 查 A 所在包是否本该导出:比如 java.lang 是导出的,但 jdk.internal.misc 默认不导出,sun.* 完全不导出
为什么 --add-exports 有时也不管用?
--add-exports 只解决“导出”问题,但 IllegalAccessError 还可能源于更底层的限制:
- 目标类本身属于未命名模块(如旧版 jar),而它试图访问 java.base 中未导出的内部类 → 此时需 --add-exports java.base/jdk.internal.misc=ALL-UNNAMED
- 目标类在自定义模块中,但它的 module-info.java 没声明 requires java.base,或没加 opens 语句 → 单靠启动参数无法绕过编译期模块约束
- 某些 JDK 内部类(如 sun.misc.Unsafe)已被标记为 @Deprecated(forRemoval=true),即使强行导出,后续版本也会彻底移除
真正安全的应对思路:避开非导出路径,而非强行打通
与其反复调试 --add-exports 参数,不如从设计上规避:
- 用标准 API 替代 JDK 内部类:比如用 VarHandle 替代 Unsafe,用 java.nio.channels.FileChannel 替代 sun.nio.ch.FileChannelImpl
- 升级依赖库版本:主流框架(Spring、Netty、Hibernate)已在 2022 年后全面适配 JPMS,弃用 sun.* / jdk.internal.* 调用
- 若必须兼容老库,统一用 --add-exports + --add-opens 组合,并限定作用域:例如 --add-exports jdk.unsupported/sun.misc=ALL-UNNAMED --add-opens java.base/jdk.internal.misc=ALL-UNNAMED
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











