父类引用指向子类对象的本质是JVM通过invokevirtual指令动态绑定方法,编译期由声明类型决定可调用方法,运行时由实际对象类型决定执行版本;字段访问静态绑定,不具多态性。

父类引用指向子类对象的本质是什么
这不是语法糖,也不是类型转换的障眼法,而是 JVM 在运行时通过 invokevirtual 指令动态绑定方法的具体实现。变量声明类型(父类)只决定编译期能调用哪些方法;真正执行哪个版本的方法,取决于堆中实际对象的运行时类型。
- 编译器只检查
父类引用是否声明了该方法——没声明就报错,不管子类有没有 - 运行时 JVM 查看实际对象所属的类,从该类的方法表中找重写后的方法入口
- 如果子类没重写,才沿继承链向上查找,直到
Object
为什么 parent.field 不会多态,但 parent.method() 会
字段访问是静态绑定,方法调用才是动态绑定。Java 规范明确:只有 final、static、private 方法和字段不参与多态;其余实例方法默认支持运行时分派。
-
parent.name取的是Parent类里定义的字段值,哪怕子类有同名字段也只是隐藏(hiding),不是覆盖(overriding) -
parent.toString()调用的是子类重写的版本,哪怕parent声明为Object - 想让字段“看起来多态”,只能靠 getter 方法间接访问
常见误用:强制转型失败与 instanceof 的必要性
父类引用无法直接调用子类独有的方法,硬转可能抛 ClassCastException。这不是设计缺陷,而是类型安全的底线。
- 调用子类特有方法前,必须先用
instanceof判断运行时类型 -
if (obj instanceof Dog) { ((Dog) obj).bark(); }—— 少这步,上线就崩 - JDK 14+ 可用模式匹配简化:
if (obj instanceof Dog d) { d.bark(); } - 集合中混存多种子类时,避免无条件强转,优先用策略接口或访问者模式
class Animal { void move() { System.out.println("move"); } }
class Bird extends Animal { void move() { System.out.println("fly"); } void chirp() { System.out.println("chirp"); } }
Animal a = new Bird();
a.move(); // 输出 "fly" —— 多态生效
// a.chirp(); // 编译错误:Animal 中没有 chirp()
if (a instanceof Bird) {
((Bird) a).chirp(); // 输出 "chirp"
}
多态不是自动帮你“猜意图”,它只保证方法调用走对的实现路径。字段、构造器、静态方法、final 方法全都不参与。写的时候少依赖“应该能转”,多想想“编译器此刻认得清什么”。










