
java 中子类声明与父类同名的实例字段时,并非覆盖父类字段,而是**隐藏(hiding)**它;同一对象内存中实际存在两个独立的 `x` 字段,分别属于父类和子类部分,通过 `this.x` 和 `super.x` 可明确访问。
在 Java 继承体系中,当子类 C 继承父类 P 并声明同名字段(如 int x)时,并不会发生字段覆盖(overriding)——因为字段不支持多态,而是触发 字段隐藏(field hiding) 机制。这是 Java 语言规范(JLS §8.3)明确定义的行为:子类中同名字段会隐藏所有可访问的父类同名字段。
关键在于:一个 C 类实例在内存中确实包含两份 x 字段——一份来自父类 P 的子对象结构(初始化于 P() 构造器),另一份来自子类 C 自身(初始化于 C() 构造器)。它们物理上独立存储,位于对象内存布局的不同偏移位置。
以下代码清晰展示了这一机制:
class P {
int x;
P() { x = 1; }
public int getX() { return x; } // 访问的是 P.x(即 this.x 在 P 上下文中)
}
class C extends P {
int x; // 隐藏了 P.x,但不删除它
C() {
super(); // 隐式调用,初始化 P.x = 1
this.x = 2; // 初始化 C.x = 2
}
void printBoth() {
System.out.println("this.x (C.x): " + this.x); // 输出 2
System.out.println("super.x (P.x): " + super.x); // 输出 1
}
}运行结果:
立即学习“Java免费学习笔记(深入)”;
this.x (C.x): 2 super.x (P.x): 1
回到原始示例中的输出差异:
- c.x → 编译期绑定到 C 类的字段,故取 C.x = 2;
- c.getX() → getX() 定义在 P 中,其方法体内的 x 指代 P.x(即 this.x 在 P 类作用域内),因此返回 1。
⚠️ 注意事项:
- 字段隐藏仅适用于实例字段;静态字段同名也会被隐藏,但访问规则不同(通过类名而非 super);
- super.x 仅可在子类非静态成员方法或构造器中使用,不可在静态上下文或外部类中通过 obj.super.x 访问(JLS §15.11.2 明确禁止);
- 为避免歧义与维护困难,强烈建议避免字段隐藏:优先使用不同名称,或通过封装(private 字段 + getter/setter)控制访问。
总结:Java 对象是“组合式”的——new C() 创建的是一个逻辑上由 P 子对象和 C 扩展部分共同构成的单一对象,其字段空间是累加的。理解字段隐藏而非覆盖,是掌握 Java 继承内存模型的关键一步。










