关于父子构造器初始化子类对象及同名成员变量的疑问
子类继承父类后同名变量的困惑
我了解到,当子类继承父类并创建子类对象时,子类构造器会隐式调用super()自动触发父类构造器。此时仅会创建一个对象,父类构造器负责初始化继承自父类的属性,子类构造器负责初始化子类特有的属性。但如下示例让我产生困惑:
父类代码:
public class P { int x; P(){ x = 1; } public int getX(){ return x; } }
子类代码:
public class C extends P { int x; C(){ x = 2; } public static void main(String[] args) { C c = new C(); System.out.println("c.x: " + c.x); System.out.println("c.getX(): " + c.getX()); } }
输出结果:
c.x: 2 c.getX(): 1
我十分困惑:明明只创建了一个对象,为何会存在两个同名的x变量?若子类隐藏了父类的x变量,为何会得到不同的输出?父类和子类读取的x究竟来自哪里?还请帮忙解惑。
核心原因:Java的**变量隐藏(Field Hiding)**机制
下面逐个解答你的疑问:
为什么一个对象里会有两个
x?
子类继承父类时,会继承父类的所有非私有成员变量,包括P类的x。但当子类C自己声明了同名的x时,这并不是"覆盖"父类的变量(覆盖只针对方法),而是隐藏了父类的x。此时这个C对象的内存结构中,同时包含两部分:- 父类
P的成员区域:存储着被隐藏的x(值为1) - 子类
C的成员区域:存储着自己声明的x(值为2)
两者属于同一个对象的不同内存区域,共存但互不干扰。
- 父类
为什么
c.x和c.getX()输出不同?- 直接访问
c.x时,编译器会根据引用的静态类型(这里是C)来解析变量,优先使用子类中声明的x,所以输出2。 getX()方法是父类P定义的,子类没有重写这个方法。当调用c.getX()时,方法内部的x是在父类的上下文里解析的,它只会访问父类区域的x(也就是被隐藏的那个),所以返回1。如果子类重写getX(),比如:
此时调用@Override public int getX() { return x; // 这里的x是子类自己的x }c.getX()就会返回2。
- 直接访问
如何直接访问被隐藏的父类变量?
在子类中可以通过super.x来显式访问父类被隐藏的变量,比如给C类加一个方法:public int getParentX() { return super.x; }调用
c.getParentX()会返回1,这就验证了父类的x确实存在于同一个对象中。
关键总结
- 变量隐藏是编译时行为:编译器根据引用的静态类型决定访问哪个变量;
- 方法覆盖是运行时行为:JVM根据对象的实际类型决定调用哪个方法;
- 子类对象包含父类的所有非私有成员,同名变量只是被隐藏,而非被替换或删除。
内容的提问来源于stack exchange,提问作者JhonM
相关产品推荐
相关产品推荐

