Java中属性隐藏是语言机制而非bug,子类同名字段不覆盖父类字段而是独立存在;应避免重复定义,统一使用父类字段,必要时用protected修饰并合理初始化,优先采用组合扩展而非同名字段。

Java 中继承关系里的属性隐藏不是 bug,而是语言机制——子类声明同名字段时,不会覆盖父类字段,而是各自独立存在两份内存空间。问题不在于“能不能”,而在于“该不该”。处理核心就一条:**避免重复定义,统一状态归属**。
删掉子类里和父类同名的字段
这是最直接有效的做法。父类已定义的公共字段(如 id、createTime、status),子类不该再写一遍。哪怕类型、注解、默认值稍有不同,也必须删掉子类字段,改用继承来的那一份。
- 如果字段需被子类修改,父类中用
protected修饰,不暴露public字段 - 子类构造器中通过
super.xxx = value或直接赋值this.xxx = value初始化 - 所有业务逻辑中统一操作
this.xxx,此时访问的就是父类继承下来的唯一字段
用组合代替同名扩展
当子类确实需要额外状态,且和父类字段语义不同(比如父类有 state 表示开关状态,子类还需记录 lastOnTime),不要定义同名 state,而是新增独立字段:
private LocalDateTime lastOnTime;private LampSpecificConfig config;- 字段命名体现归属,避免歧义
借助工具提前拦截
单靠人工审查容易遗漏,尤其在多人协作的老项目中。建议配置防御性机制:
立即学习“Java免费学习笔记(深入)”;
- IDE(IntelliJ/VS Code)开启 “Field hides another field” 警告,实时标黄提示
- CI 流水线集成 Checkstyle 规则
HiddenField,禁止子类声明与父类同名的非静态字段 - 单元测试中增加断言:
assertThat(((SubClass) obj).field).isEqualTo(obj.field),验证父子引用读取的是同一份数据
调试时注意引用类型的影响
字段访问不走多态,只看变量声明类型。同一个对象,用父类引用和子类引用读出来的同名字段值可能完全不同:
Parent p = new Child(); System.out.println(p.field); // 读父类字段Child c = new Child(); System.out.println(c.field); // 读子类字段(如果存在)- 调试时别只看
toString()输出,要分别打印((Child)p).field和p.field对比


















