a.x 访问父类字段是因为字段访问基于编译时类型(静态绑定),不支持多态;子类同名字段是隐藏而非覆盖,B实例内存中同时存在A.x和B.x两个独立字段,a作为A类型引用只能访问A.x。

子类对象赋值给父类引用(如 A a = new B();)后,调用方法走子类重写版本,但访问同名字段却得到父类的值——这不是bug,是Java明确设计的行为,核心在于方法支持动态绑定,字段只做静态绑定。
为什么 a.x 访问的是父类字段,而不是子类的?
因为字段没有多态性。Java 编译器在编译阶段就根据引用变量的声明类型(这里是 A)决定访问哪个字段。即使实际对象是 B 的实例,a.x 也只会查找 A 类中定义的 x 字段。
子类中声明的同名字段(如 int x = 20;)不是覆盖父类字段,而是隐藏(field hiding):它和父类字段在内存中并存,各自独立。一个 B 实例内部其实有两个 x —— A.x(值为10)和 B.x(值为20)。
怎么验证字段确实共存?
在子类里写个方法,分别打印 this.x 和 super.x:
立即学习“Java免费学习笔记(深入)”;
-
this.x→ 输出子类字段值(如 20) -
super.x→ 输出父类字段值(如 10)
这直接证明两者物理存在且互不干扰。而通过父类引用 a 访问 x,只能看到 A 视角下的那个字段。
getter 方法返回的为什么还是父类字段值?
假设 A 中有 public int getX() { return x; },子类 B 没有重写它。那么 a.getX() 调用的是 A 类的方法体,其中的 this.x 绑定到 A 类的字段定义,结果自然是 10。
即使你把 a 指向 B 实例,只要没重写 getX(),方法体里的 this.x 就永远指 A 的字段——这是由方法定义所在类决定的,不是由运行时对象决定的。
面试官想考察什么?
他们关注你是否分清两个关键点:
- 方法调用看运行时类型(子类重写就执行子类逻辑)
- 字段访问看编译时类型(引用声明是哪个类,就取哪个类的字段)
- 理解“隐藏”不是“覆盖”,避免在子类中声明同名字段,推荐用不同名字段 + getter/setter 封装
这个区别体现了 Java 对多态的严格限定:只有非静态、非 private 的实例方法才参与动态分派。


















