Java多态调用核心依赖vtable:JVM类加载时为每个非final类生成静态vtable,存储非private/static/final实例方法的入口地址;子类vtable继承父类结构并覆盖重写方法槽位;invokevirtual指令根据对象实际类型查vtable固定索引完成动态单分派。

Java 中方法重写(Override)的多态调用,核心依赖于运行时的虚方法表(Virtual Method Table,简称 vtable),这是 JVM 实现动态绑定的关键机制。它让父类引用指向子类对象时,能自动调用子类中重写后的方法,而非编译期确定的父类方法。
虚方法表是什么?
每个类在 JVM 加载时,都会为其生成一张虚方法表,表中按声明顺序存放该类所有可被重写(非 private、static、final)的实例方法的实际入口地址(即具体实现的字节码位置)。子类的 vtable 会继承父类的结构,并将被重写的方法项替换为子类自己的实现地址。
例如:
- 类 Animal 有方法 makeSound(),其 vtable 第 3 项指向 Animal.makeSound 的字节码;
- 子类 Dog 重写了 makeSound(),它的 vtable 第 3 项就改为指向 Dog.makeSound 的字节码;
- 若 Dog 新增了方法 fetch(),则该方法会追加到 vtable 末尾(不覆盖父类表结构)。
invokevirtual 指令如何触发 vtable 查找?
当代码执行类似 animal.makeSound()(其中 animal 是 Animal 类型引用,实际指向 Dog 对象)时,JVM 不会直接跳转到 Animal 的实现,而是:
立即学习“Java免费学习笔记(深入)”;
- 根据 对象实际类型(Dog),定位其对应的虚方法表;
- 根据方法签名(如
makeSound()V)计算该方法在表中的索引(由类加载阶段静态确定); - 从 Dog 的 vtable 中取出该索引处的地址,跳转执行。
这个过程发生在运行时,与变量声明类型(Animal)无关,只取决于对象真实类型(Dog)——这就是动态绑定的本质。
哪些方法不会进虚方法表?
vtable 只管理可被多态调用的实例方法。以下情况不参与 vtable 调度:
- private 方法:隐式 final,仅限本类内联调用,编译期静态绑定;
-
static 方法:属于类而非实例,通过类符号引用直接解析,调用指令是
invokestatic; - final 实例方法:虽可被继承,但禁止重写,JVM 可能内联优化,不放入 vtable 多态调度链;
- 构造方法:不能被重写,也不通过 vtable 调用。
补充说明:接口方法略有不同
接口不使用传统 vtable,而是用接口方法表(itable)。一个类实现多个接口时,JVM 为其生成 itable,记录各接口方法到实际实现的映射。调用接口方法(invokeinterface)需先查 itable,再定位实现。虽然机制不同,目标一致:运行时根据实际对象类型分派正确方法。


















