
本文深入解析java中继承方法调用时的字段访问、方法分派与可见性规则,阐明b.methodone()为何实际使用子类b的字段m和重写后的increase(),并澄清静态类型与动态类型在多态中的关键作用。
本文深入解析java中继承方法调用时的字段访问、方法分派与可见性规则,阐明b.methodone()为何实际使用子类b的字段m和重写后的increase(),并澄清静态类型与动态类型在多态中的关键作用。
在Java面向对象编程中,继承方法的行为常被初学者误解为“完全属于父类”,实则其运行时行为由动态绑定(Dynamic Binding) 和 访问作用域(Visibility Scope) 共同决定。核心原则是:方法体在编译期确定,但其中涉及的非私有实例方法调用和实例字段访问,在运行期依据实际对象类型动态解析——前提是字段未被隐藏、方法未被声明为final或static。
一、字段访问:子类字段会“隐藏”而非“覆盖”父类同名字段
在示例中,类B声明了私有字段private int m;,而其父类A也定义了private int m;。注意:字段不支持多态,不存在“覆盖(override)”,只有“隐藏(hiding)”。当b.methodOne(6)执行时:
- m -= i; 中的 m 是编译期静态解析的结果:因methodOne定义在A中,该语句访问的是A自身的私有字段m(即super.m),而非B的m;
- 同理,n++ 在visibilityTest()中访问的也是A自己的n。
✅ 正确理解:继承方法内部访问的实例字段,永远是其定义所在类(此处为A)声明的字段,与调用对象的实际类型无关。子类同名字段仅在其自身方法中可见,对父类方法透明。
// 在 A.methodOne() 内部: m -= i; // ✅ 访问的是 A.m(super.m),不是 B.m // B.m 对 methodOne 来说是不可见的,即使 b 是 B 的实例
二、方法调用:遵循动态绑定(运行时多态)
与字段不同,非私有、非静态、非final的实例方法调用严格遵循动态绑定。当b.methodOne(6)执行时:
立即学习“Java免费学习笔记(深入)”;
- methodOne 本身未被B重写,因此执行的是A中定义的版本;
- 但其中的increase(i)是虚方法调用(virtual call):编译器生成指令在运行时检查b的实际类型(B),发现B重写了increase,于是动态分派到B.increase(i)。
// A.methodOne(int i) 的执行流程(b 是 B 实例):
public void methodOne(int i) {
m -= i; // → 访问 A.m(值为 4,因 super(n)=4)
increase(i); // → 动态绑定 → 调用 B.increase(6),修改 B.m += 6
}⚠️ 注意:B.increase() 修改的是 B 自己的 m 字段(this.m),而 A.methodOne() 中的 m -= i 修改的是 A 的 m。二者互不影响——这正是字段隐藏带来的常见陷阱。
三、可见性(Visibility):继承方法沿用定义处的访问权限上下文
关于b.visibilityTest()能否成功调用:
✅ 可以。因为visibilityTest()是A中定义的public方法,其内部对private int n的访问,是在A类的词法作用域内进行的。Java规定:一个类的方法可以自由访问本类声明的所有成员(含private),无论该方法是被本类还是子类的对象调用。
换言之,可见性检查发生在编译期,依据的是方法定义所在的类,而非调用方的类。因此visibilityTest()作为A的方法,天然拥有访问A.n的权限,与b是B的实例无关。
| 场景 | 是否允许 | 原因 |
|---|---|---|
| b.visibilityTest() | ✅ 是 | 方法定义在A,访问A.n合法 |
| B中直接写 n++ | ❌ 否 | B无法访问A的private n |
| B中重写visibilityTest()并访问n | ❌ 编译失败 | 重写后方法属于B,无权访问A.n |
四、静态类型 vs 动态类型:理解多态的基石
- 静态类型(Static Type):变量声明的类型,如 A b → 编译器据此检查可调用哪些方法(即方法签名是否存在)。若A中没有foo(),则b.foo()编译失败。
- 动态类型(Dynamic Type):new创建的对象实际类型,如 new B() → 运行时JVM据此决定调用哪个方法实现(动态绑定)。
A b = new B(1, 4); // 静态类型:A;动态类型:B b.methodOne(6); // ✅ 编译通过(A有methodOne)→ 运行时执行A.methodOne,但increase调用B版本 // b.privateMethod(); // ❌ 编译失败:静态类型A无此方法
总结:三条黄金法则
- 字段访问看定义处:继承方法内部访问的实例字段,始终是其定义所在类声明的字段(字段隐藏,非覆盖);
- 方法调用看运行时:非私有实例方法调用,运行时依据对象实际类型动态分派(方法重写,支持多态);
- 可见性检查看声明处:方法体内的成员访问权限,由该方法定义所在的类决定,与调用对象类型无关。
掌握这三点,即可准确预测任何继承场景下字段与方法的行为,避免因混淆“字段隐藏”与“方法覆盖”而导致的逻辑错误。








