java中子类与父类同名成员变量是隐藏而非覆盖,编译时按就近原则决定访问哪个变量;new child()对象内存中同时存在父类value(10)和子类value(20),通过super.value可访问父类变量。

Java 中子类与父类同名成员变量既不是覆盖,也不是“谁替换了谁”,而是隐藏(Field Hiding),访问时按编译时类型 + 就近原则决定读取哪个变量。理解这点,就能避开多态失效、值不一致、序列化异常等典型坑。
就近原则:访问变量时的查找顺序
当代码中出现一个变量名(比如 value),JVM 编译器会按固定顺序查找它的定义位置:
- 先看当前方法内有没有同名局部变量
- 没有就查当前类(子类)有没有同名成员变量
- 还没有就向上查父类的成员变量(逐级向上,直到
Object)
这个过程发生在编译期,是静态绑定。例如:
class Parent { int value = 10; }
class Child extends Parent { int value = 20; void show() { System.out.println(value); } }
在 show() 中写 value,直接输出 20——因为子类自己的 value 更“近”,无需 this. 也能命中。
隐藏的本质:两个变量共存,而非覆盖
子类声明 int value = 20; 并不会删掉或改写父类的 value。在内存中,new Child() 创建的对象里实际包含两份 value:
- 属于
Parent子对象部分的value(值为 10) - 属于
Child自身部分的value(值为 20)
它们地址不同、互不影响。所谓“隐藏”,只是让子类变量遮蔽了父类变量的默认可见性。就像书架上同一位置放了两本书,你伸手拿时,默认拿到上面那本——但下面那本还在。
为什么多态对变量不生效?关键在绑定时机
方法调用走的是运行时动态绑定:引用类型是 Parent,但实际对象是 Child,就会执行 Child 重写后的方法。
而变量访问走的是编译时静态绑定:变量读取结果只取决于引用的声明类型:
Parent p = new Child(); System.out.println(p.value); // 输出 10(p 是 Parent 类型) Child c = new Child(); System.out.println(c.value); // 输出 20(c 是 Child 类型)
哪怕 p 指向的是 Child 实例,只要声明类型是 Parent,就只能看到父类那份 value。这不是 bug,是 Java 明确设计的行为。
怎么安全访问被隐藏的父类变量?
在子类内部,有三种明确方式区分访问目标:
-
value→ 默认访问子类自己的变量(就近) -
this.value→ 显式访问本类变量(效果同上,强调归属) -
super.value→ 强制访问父类被隐藏的变量
注意:super.xxx 只能在非静态上下文中使用;如果父类字段是 private,子类根本无法用 super 访问,此时同名声明就是完全独立的新变量,谈不上隐藏。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











