java中变量只有“隐藏”而非“覆盖”,子类同名字段会在内存中独立分配,与父类字段互不干扰;静态和实例变量均遵循此规则,正确做法是删除重复字段、统一由父类管理状态。

Java 中变量没有“覆盖”机制,只有“隐藏”(hiding)——这是关键前提。方法可以被重写(overriding),但字段(实例变量、静态变量)永远不能被覆盖,只能被同名声明所隐藏。理解这一点,才能避免状态错乱、条件判断失效等典型问题。
变量隐藏的本质是内存独立,不是替换
当子类声明一个与父类同名的字段时,JVM 会在子类实例中额外分配一块内存空间,和父类字段互不干扰。两者同时存在,只是访问路径不同:
- 用 父类引用(如
Switchable s = new Lamp();)访问s.state→ 读取的是父类字段 - 用 子类引用(如
Lamp lamp = new Lamp();)访问lamp.state→ 读取的是子类字段 - 在子类内部写
this.state或直接写state→ 默认访问子类自己的字段(即被隐藏的那一份) - 想访问父类字段?必须显式写
super.state
静态变量和实例变量都遵循隐藏规则
隐藏不区分实例还是静态,也不要求类型一致。甚至允许交叉隐藏:
- 子类
static int x可以隐藏父类int x(静态隐藏实例) - 子类
int x可以隐藏父类static int x(实例隐藏静态) - 父类
public String name被子类private String name声明后,依然算隐藏(访问权限不影响隐藏关系)
注意:静态方法也只支持隐藏(同名 static 方法),不支持重写;而实例方法才支持真正的重写(多态调用)。
正确做法:删掉子类重复字段,统一由父类管理状态
面向对象设计要求“状态归属清晰”。若多个子类都要维护类似 state、name、id 这类通用属性,它们就该定义在父类中,子类只负责行为扩展:
- 父类中声明
protected State state;(避免public直接暴露) - 子类构造器里通过
super()或this.state = ...初始化继承来的字段 - 所有方法(包括
turn_on())都操作this.state,此时它就是那一份共享字段 - 如需子类特有状态,用新字段名(如
private Color bulbColor;),而不是重名
辅助防护:用工具提前拦截隐藏风险
人工审查容易遗漏,建议启用以下机制:
- IDE 设置:开启 “Field hides another field” 警告(IntelliJ / Eclipse 均支持)
- Checkstyle 规则:
HiddenField检查器可强制禁止子类声明与父类同名字段 - Code Review 时重点扫视子类中是否出现父类已定义的字段名
只要子类不新增同名字段,多态调用 + 字段访问就能保持逻辑一致。这不是语法限制,而是设计纪律。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











