
本文深入解析java向上转型时方法调用与字段访问行为不一致的根本原因:方法具有动态绑定(运行时多态),而实例字段不支持重写,仅发生静态遮蔽。
本文深入解析java向上转型时方法调用与字段访问行为不一致的根本原因:方法具有动态绑定(运行时多态),而实例字段不支持重写,仅发生静态遮蔽。
在Java中,当执行 A x = new B(); 这一向上转型(upcasting)操作时,变量 x 的编译时类型(静态类型)是 A,而运行时类型(实际对象)是 B。这一看似简单的赋值,却揭示了Java面向对象机制中两个关键但易被混淆的概念:方法重写(Overriding) 与 字段遮蔽(Field Hiding)。
方法调用:动态绑定,走运行时类型
Java中实例方法默认具有多态性。调用 x.shout() 时,JVM在运行时根据 x 实际指向的对象类型(即 B)查找并执行 B.shout() —— 这就是动态绑定(Dynamic Binding)。由于 B 重写了 shout() 方法,且其内部 return i; 访问的是 B 类自身的字段 i(值为20),因此输出 20。
字段访问:静态绑定,走编译时类型
与方法不同,Java中字段不支持重写(Overriding),只支持遮蔽(Hiding)。当 B 声明同名字段 int i = 20; 时,并非覆盖父类 A.i,而是在 B 对象内存布局中新增一个独立字段,与 A.i 并存。此时 x.i 的解析完全由变量声明类型 A 决定(编译期确定),JVM直接访问 A 类定义的 i 字段(值为10),故输出 10。
以下代码清晰展示了字段共存与访问方式:
立即学习“Java免费学习笔记(深入)”;
A x = new B(); System.out.println(x.i); // → 10(访问 A.i,编译时类型决定) System.out.println(((B) x).i); // → 20(强制转为 B 类型后访问 B.i) System.out.println(x.shout()); // → 20(动态绑定到 B.shout(),返回 B.i)
最佳实践与注意事项
- ✅ 优先使用私有字段 + 公共getter/setter:避免字段遮蔽带来的歧义。例如将
i设为private,通过getI()方法统一访问,天然支持多态。 - ⚠️ 避免公开同名字段继承:若必须使用同名字段,请明确使用
super.i(访问父类字段)或this.i(访问当前类字段)消除歧义。 - ❌ 切勿依赖字段多态:
x.i永远不会动态分派到子类字段——这是语言规范,而非bug。
总结来说:“方法看new谁,字段看声明谁”。理解这一原则,是掌握Java多态本质的关键一步。


















