
java中,子类同名字段会隐藏父类字段(编译时绑定),而方法重写则实现运行时多态——这导致通过父类引用访问字段和调用方法时行为不同:前者取父类字段值,后者执行子类方法体。
java中,子类同名字段会隐藏父类字段(编译时绑定),而方法重写则实现运行时多态——这导致通过父类引用访问字段和调用方法时行为不同:前者取父类字段值,后者执行子类方法体。
在Java面向对象机制中,字段(成员变量)不支持多态,而方法支持。这是理解上述代码输出 10 和 20 的核心关键。
回顾示例代码:
A x = new B(); // 向上转型:父类引用指向子类对象 System.out.println(x.i); // 输出 10 → 访问的是 A 类中定义的字段 i System.out.println(x.shout()); // 输出 20 → 调用的是 B 类重写后的 shout() 方法
字段访问:静态绑定,基于引用类型
当执行 x.i 时,JVM 根据引用变量的声明类型(A) 决定访问哪个字段。由于 x 声明为 A 类型,编译器直接解析为 A.i,即使实际对象是 B 的实例。B.i 并未覆盖 A.i,而是隐藏(field hiding)了它——两个字段在内存中独立存在(A 中的 i 和 B 中新增的 i),彼此无继承关系。
方法调用:动态绑定,基于实际对象类型
而 x.shout() 触发的是动态方法调度(Dynamic Method Dispatch):编译期检查 A 是否有 shout() 方法(有,故通过编译),运行期根据 x 实际指向的对象类型(B)决定执行 B.shout()。由于 B 重写了该方法,且其内部 return i; 中的 i 指向的是 B 自己的字段(即 this.i),因此返回 20。
立即学习“Java免费学习笔记(深入)”;
正确实践建议
-
✅ 优先封装字段:将
i声明为private,并通过protected或public的 getter/setter 控制访问:class A { private int i = 10; public int getI() { return i; } public int shout() { return getI(); } } class B extends A { private int i = 20; // 不再隐藏,而是独立私有字段 @Override public int getI() { return i; } // 显式重写逻辑 } -
⚠️ 若必须使用同名公有字段,请明确区分作用域:
class B extends A { int i = 20; int shout() { System.out.println(super.i); // 10 → 父类字段 System.out.println(this.i); // 20 → 子类字段 return this.i; } } ❌ 避免字段隐藏:它易引发歧义,破坏可维护性;Java 设计哲学强调“用方法表达行为,用封装保护状态”。
总结:字段看声明类型,方法看实际类型——这是 Java 多态性的基石之一,也是初学者常混淆的关键分水岭。


















