illegalaccesserror 是运行时 jvm 检测到非法访问(如子类访问已不存在/不可见的父类字段)抛出的严重错误,非编译期问题,通常由类结构不一致、模块限制或热部署导致,无法捕获恢复。

IllegalAccessError 不是编译期报错,而是在运行时由 JVM 检测到非法访问后抛出的严重错误。它和 IllegalAccessException 不同——后者是受检异常,可被捕获;而前者是 Error 子类,属于 JVM 层面的访问违规,程序通常无法恢复。
为什么子类会“突然”访问不到父类私有变量?
根本原因不是子类代码改了,而是父类字节码在运行时被替换或重新加载,导致类结构不一致:
- 父类 JAR 包升级后,私有字段被重命名、删除,或访问方式从 getter 改为直接字段读取
- 使用 OSGi、Spring Loaded、JRebel 等热部署机制时,父类与子类由不同类加载器加载,造成“同名但不同类”的假象
- 动态代理或字节码增强(如 Lombok @Data 生成的 getter/setter)在编译期存在,但运行时因 classpath 混乱缺失对应逻辑
protected 字段被子类访问却报 IllegalAccessError?
这看似矛盾,实则常见于跨模块或跨包继承场景:
- Java 9+ 模块系统下,即使字段声明为
protected,若子类所在模块未对父类模块opens或exports对应包,JVM 仍拒绝访问 - 父类定义在 unnamed module(如传统 classpath),子类在 named module 中,且未显式开放该包,
protected成员对子类不可见 - 子类内部类试图通过
super.xxx访问父类protected字段,但该字段实际已被父类重构为私有 + package-private setter,字节码层面已不存在原字段
如何定位和规避这类陷阱?
重点不在“怎么绕过”,而在“怎么提前发现并设计防御”:
- 禁止子类直接读写父类字段——无论 public/protected,默认只通过 public/protected 方法交互
- 父类中所有对外暴露的状态,统一走 getter/setter,避免子类依赖字段名或内存布局
- 使用
jdeps -s检查模块依赖,确认requires和opens声明完整 - 在 CI 阶段运行
javap -private ParentClass与子类编译产物比对字段签名,识别结构性变更 - 若必须反射访问,优先用
MethodHandles.lookup().findGetter()替代Field.setAccessible(true),前者受模块系统管控更明确
一个典型失败案例还原
假设 v1.0 的 DatabaseConfig 类含 protected String url;,子类 MySqlConfig 直接使用 super.url 构建连接串。v2.0 中该字段改为 private final String connectionUri,并新增 getJdbcUrl() 方法。若子类未同步升级,运行时 JVM 发现字节码中 super.url 引用已不存在,立即抛出 IllegalAccessError ——不是权限不够,而是符号根本找不到。











