
本文详解java中继承方法调用时的实际执行逻辑——方法调用遵循动态绑定(多态),实例字段存在遮蔽(shadowing)而非覆盖,而访问权限始终以定义该成员的类(即声明处)的可见性为准。
本文详解java中继承方法调用时的实际执行逻辑——方法调用遵循动态绑定(多态),实例字段存在遮蔽(shadowing)而非覆盖,而访问权限始终以定义该成员的类(即声明处)的可见性为准。
在Java面向对象编程中,理解“继承方法究竟使用哪个类的字段、方法和访问权限”是掌握多态与封装机制的关键。许多初学者易混淆静态类型(compile-time type) 与动态类型(runtime type) 的作用,误以为继承方法会“固定绑定”到父类的实现。实际上,Java严格遵循动态方法分派(Dynamic Method Dispatch) 原则,同时对字段和访问控制采取不同规则。
✅ 核心结论先行
- 方法调用 → 动态绑定:b.methodOne(6) 调用的是 A 中定义的 methodOne 代码,但其中对 increase(i) 的调用会动态绑定到 B 类的重写版本(若存在);
- 实例字段 → 静态绑定(遮蔽):methodOne 内部的 m 引用的是当前对象所属类中定义的 m 字段(即 B 的私有 m),而非 A 的 m —— 这不是“覆盖”,而是字段遮蔽(field shadowing);
- 访问权限 → 以声明处为准:visibilityTest() 可被 b 正常调用,且其内部对 private int n 的访问合法,因为n 的 private 权限是在 A 中声明的,而 visibilityTest 也在 A 中定义,二者同属一个编译单元,不受 b 的实际类型影响。
? 结合示例深度解析
以下为关键代码片段及逐行说明:
A b = new B(1, 4); // 静态类型:A;动态类型:B b.methodOne(6);
① methodOne 的执行流程(动态绑定)
public void methodOne(int i) {
m -= i; // ← 注意:此处的 m 是哪个?答案:B 类的 m(遮蔽了 A 的 m)
increase(i); // ← 动态绑定:实际调用 B.increase(i),因 B 重写了该方法
}- m -= i:虽然 methodOne 定义在 A 中,但 Java 字段访问不具有多态性。由于 B 声明了同名私有字段 private int m;,它遮蔽(shadow) 了父类 A 的 m。在 B 实例上调用时,该语句操作的是 B 自己的 m 字段(值为 1 + 1 = 2),而非 A 的 m(值为 4)。
- increase(i):这是虚方法调用(virtual call)。JVM 在运行时检查 b 的实际类型(B),发现 B 重写了 increase,于是执行 B.increase(i)(即 m += i),而非 A.increase(i)(m += 2*i)。
⚠️ 关键提醒:字段遮蔽 ≠ 方法重写。字段无法被“重写”,只能被同名字段遮蔽;而方法可通过 @Override 实现动态分派。
② 访问权限:为什么 b.visibilityTest() 合法?
public void visilibityTest() {
n++; // ✅ 合法:n 是 A 的 private 字段,但 visibilityTest 也在 A 中定义
}- private 修饰符的含义是:“仅在声明该成员的类内部可访问”。
- visibilityTest 是 A 的成员方法,n 是 A 的 private 字段,二者同属类 A,因此方法内访问完全合法。
- 即使通过 B 的实例调用(b.visibilityTest()),只要调用链未离开 A 的代码范围,private 约束依然满足。访问权限检查发生在编译期,依据的是方法定义位置,而非调用者类型。
? 实践注意事项与最佳实践
- 避免字段遮蔽:在子类中声明与父类同名的实例字段(尤其 private)极易引发逻辑错误。推荐统一使用 protected 或包级访问字段,并通过 getter/setter 控制访问;或直接在子类中使用新字段名。
- 明确区分 super. 与 this.:若需在子类中显式访问被遮蔽的父类字段,必须使用 super.m(前提是字段非 private);private 字段永远无法被子类直接访问,只能通过父类提供的 protected/public 方法间接操作。
- 多态依赖重写,而非继承本身:继承只是复用代码结构,真正的运行时多态必须通过方法重写(override) 实现。未被重写的方法,其全部逻辑(包括内部字段引用和方法调用)均按定义类(父类)的语义执行——但被重写的方法调用会被动态替换。
-
编译期 vs 运行期检查:
- 编译器检查:静态类型决定能否调用某方法/访问某字段(如 A b 允许调用 A 中 public/protected 成员);
- JVM 检查:动态类型决定具体执行哪个重写版本的方法(如 b.increase() 执行 B.increase())。
✅ 总结一句话
Java中,方法调用走动态绑定(看对象实际类型),字段访问走静态绑定(看变量声明类型+遮蔽关系),而访问权限检查严格依据成员的声明位置(类作用域)——三者独立运作,共同构成安全、灵活的继承模型。
立即学习“Java免费学习笔记(深入)”;








