Java多态中父类引用指向子类对象时实现动态方法绑定,需同时满足继承关系、方法被正确重写、通过父类引用调用;static/private/final方法因编译期绑定或不可重写而不参与动态绑定。

Java 多态中,父类引用指向子类对象时能实现动态方法绑定,核心在于编译看左边(引用类型),运行看右边(实际对象类型),且要求方法满足:非 private、非 static、非 final,并被子类重写(override)。
必须满足的三个前提条件
动态绑定不是自动发生的,需同时满足:
- 继承关系存在:子类 extends 父类,或实现同一接口
- 方法被正确重写:子类中用相同签名(方法名 + 参数列表)和兼容返回类型重新定义父类的非私有实例方法
- 通过父类引用调用该方法:例如 Animal a = new Dog(); a.sound();,而非 Dog d = new Dog(); d.sound();(后者是静态绑定)
为什么 static / private / final 方法不参与动态绑定
这些方法在编译期就确定了调用目标,与实际对象无关:
- static 方法:属于类,绑定依据是引用的声明类型。即使子类定义同名 static 方法,也只是隐藏(hiding),不是重写
- private 方法:无法被继承,子类中的同名方法是全新方法,与父类无关系
- final 方法:明确禁止重写,编译器直接内联或固定调用父类版本
底层机制:虚方法表(vtable)简述
JVM 为每个类维护一张虚方法表,表中存放该类所有可被重写的实例方法的入口地址。当执行 a.sound() 时:
立即学习“Java免费学习笔记(深入)”;
- 编译器查到 a 是 Animal 类型,确认 sound() 是可重写方法
- 运行时根据 a 实际指向的 Dog 对象,定位其 vtable
- 查 vtable 中 sound() 对应的地址,跳转到 Dog.sound() 执行
一个典型示例
class Animal { void sound() { System.out.println("Animal makes a sound"); } }
class Dog extends Animal { @Override void sound() { System.out.println("Dog barks"); } }
class Cat extends Animal { @Override void sound() { System.out.println("Cat meows"); } }
Animal a1 = new Dog();
Animal a2 = new Cat();
a1.sound(); // 输出 "Dog barks"
a2.sound(); // 输出 "Cat meows"
虽然 a1 和 a2 声明类型都是 Animal,但 JVM 在运行时识别出它们分别指向 Dog 和 Cat 实例,从而调用各自重写的 sound() 方法。


















