Java多态通过动态绑定实现,即运行时根据对象实际类型查虚方法表(vtable)确定调用方法;需满足继承、向上转型、调用被重写非static/final/private方法三个条件。

Java 方法重写(Override)在运行期实现多态,靠的是动态绑定(Dynamic Binding),也叫运行时绑定(Runtime Binding)。它不是在编译时决定调用哪个方法,而是在程序运行时,根据实际对象的类型(而非引用变量的声明类型),通过 JVM 的虚方法调用机制来确定执行哪个版本的方法。
动态绑定的核心:虚方法表(vtable)
JVM 为每个类维护一张虚方法表
- 表中按声明顺序存放该类所有可被重写(非 static、非 final、非 private)的实例方法的入口地址;
- 子类继承父类方法时,若进行了重写,子类 vtable 中对应方法项会被替换成子类自己的方法实现地址;
- 当执行
invokevirtual字节码指令(普通实例方法调用)时,JVM 查找对象实际所属类的 vtable,再按方法签名定位并跳转到具体实现。
触发动态绑定的必要条件
必须同时满足以下三点,才能启用运行时多态:
- 有继承关系(或实现接口),子类重写了父类的非私有、非静态、非 final 方法;
- 使用父类(或接口)类型的引用指向子类对象(即发生向上转型,如
Animal a = new Dog();); - 通过该引用调用被重写的方法(如
a.sound();)——此时编译器只校验方法是否存在,不决定具体实现。
为什么 static / final / private 方法不参与动态绑定?
它们在编译期就绑定了目标方法,属于静态绑定:
立即学习“Java免费学习笔记(深入)”;
-
static 方法属于类,调用依赖引用的声明类型(
Parent.say()永远调父类方法); - final 方法禁止重写,JVM 可直接内联或硬编码跳转;
- private 方法无法被继承,子类里同名方法是全新方法,与父类无关。
一个典型例子看运行时如何选择
假设:
class Animal { void move() { System.out.println("Animal moves"); } }
class Dog extends Animal { void move() { System.out.println("Dog runs"); } }
class Cat extends Animal { void move() { System.out.println("Cat jumps"); } }
Animal a1 = new Dog();
Animal a2 = new Cat();
a1.move(); // 输出 "Dog runs"
a2.move(); // 输出 "Cat jumps"
虽然 a1、a2 声明类型都是 Animal,但 JVM 在运行时发现 a1 实际是 Dog 实例,就查 Dog 类的 vtable 找到 move() 地址;a2 同理查 Cat 的 vtable —— 这就是多态的实质。


















