Java继承成员变量访问疑问:C类实例v为何输出存在差异?
为什么C类实例在不同访问场景下的x输出不同?
这是个非常典型的Java**成员变量隐藏(Variable Hiding)**问题,和大家熟悉的方法多态逻辑完全不一样,咱们一步步把它拆明白:
先理清类结构里的变量关系
咱们先看代码里的类定义:
class A { int x = 1; } class B extends A { } class C extends B { int x = 2;}
- 类A定义了成员变量
x=1; - 类B继承A,没有重新定义
x,所以它的x就是从A继承来的1; - 类C继承B,自己定义了
int x=2——这里C的x并不是"重写"父类的x,而是隐藏了父类A中的x。Java里成员变量不存在重写,只有隐藏。
两种访问场景的差异分析
场景1:直接用C类引用访问
C v = new C(); System.out.println(v.x); // 输出2
这里变量v的编译时类型和运行时类型都是C,编译器在编译阶段就确定要访问C类自己定义的x,所以输出2,这个逻辑很直观。
场景2:用父类A的数组引用访问
A [] a = { new A(), new B(), new C()}; for (int i=0; i<3; ++i) System.out.println(a[i].x); // 三次都输出1
这里的关键是:Java的成员变量是静态绑定的——也就是说,编译器在编译时会根据变量的编译时类型来确定要访问哪个变量,而不是看运行时实际的对象类型。
数组a的类型是A[],所以数组里每个元素a[i]的编译时类型都是A,编译器会直接绑定到A类定义的x=1。哪怕a[2]运行时实际是C类的实例,也不会访问C类的x,因为编译阶段已经确定了要找A的x。
关键区别:变量隐藏 vs 方法重写
一定要记住:
- 方法是动态绑定(多态):编译时看父类方法,运行时实际调用子类重写的方法;
- 成员变量是静态绑定:编译时就确定要访问的变量,完全看引用的编译时类型,和运行时对象无关。
如果把代码改成用方法来返回x,结果就会不一样:
class A { int getX() { return 1; } } class B extends A { } class C extends B { int getX() { return 2;} } // 此时遍历A数组调用getX(),会输出1、1、2
内容的提问来源于stack exchange,提问作者Ryukaizan
相关产品推荐
相关产品推荐

