变量遮蔽是编译期静态绑定行为,需通过引用类型判断访问目标:Parent p = new Child(); p.state访问父类字段,Child c = new Child(); c.state访问子类字段,IDE悬停提示“shadows field”可快速识别。

直接看变量访问时的编译类型,再比对实际声明位置——遮蔽不是运行时错误,而是编译期就定死的静态绑定行为,所以排查关键在“谁在看、从哪看”。
确认是否真发生了字段遮蔽
检查子类是否显式声明了与父类完全同名、同类型的实例变量(public/protected/package-private),尤其是 非 private 字段。例如:
- 父类有
public State state;,子类也写public State state;→ 遮蔽成立 - 父类是
private String name;,子类再声明String name;→ 不算遮蔽(因父类字段本就不能被子类直接访问) - 子类只重写了 getter 方法但没声明同名字段 → 不是遮蔽,是正常继承
验证访问路径与实际读取值
用不同引用类型调用同一变量,观察输出差异:
-
Parent p = new Child(); System.out.println(p.state);→ 输出父类字段值 -
Child c = new Child(); System.out.println(c.state);→ 输出子类字段值 - 在子类方法中加日志:
System.out.println("this.state=" + this.state + ", super.state=" + super.state);→ 可直观看到两个独立值
借助工具快速定位
IDE 悬停变量名通常会提示 “shadows field from superclass”,这是最直接信号;也可启用编译器警告:
- Java 编译参数加
-Xlint:shadow,编译时会报出遮蔽警告 - IntelliJ 或 Eclipse 中开启 “Field hides a superclass field” 检查项
- 静态分析工具如 SonarQube 或 PMD 也能识别此类模式
修复策略与替代写法
不建议保留同名字段,优先采用以下方式消除歧义:
- 子类删掉重复字段声明,统一通过
super.state操作父类状态 - 若需扩展状态,改用新字段名(如
lampBrightness)并配合注释说明职责 - 把状态封装进 protected 的 getter/setter 中,子类重写逻辑而非覆盖字段
- 抽象类中将字段设为
final或移至构造函数初始化,避免被无意替换











