虚方法表在类加载进方法区时生成:jvm为每个非private、非static、非final的实例方法构建静态vtable,子类继承父表结构并覆盖重写方法槽位,调用时依据对象实际类型查表完成动态单分派。

Java 继承中,虚方法表(Virtual Method Table,简称 VTable)是支撑多态和动态方法调用的底层核心机制。它不是语法糖,而是 JVM 在类加载阶段就构建好的真实数据结构,直接决定“调用哪个方法”。
虚方法表什么时候生成?
每个类在被 JVM 加载进方法区时,就会为其生成一张虚方法表。这张表是 Class 对象的一部分,只包含满足以下三个条件的方法:
- 非 private(否则子类看不见,无法重写或继承)
- 非 static(静态方法属于类,不参与对象多态)
- 非 final(final 方法禁止重写,也就无需动态分派)
比如 public void run()、protected String getName() 会被放进表里;而 private void init() 或 static void main(String[]) 就不会。
子类的虚方法表怎么组织?
子类的虚方法表不是空表,也不是全拷贝父类的表,而是按如下逻辑构建:
- 先复制父类虚方法表中所有可继承的方法(即上面三条件都满足的方法)
- 如果子类重写了某个父类方法,就把对应槽位(slot)里的地址替换成子类自己的实现
- 子类新增的虚方法,追加到表尾
例如:Animal 有 makeSound(),Dog 重写了它,Cat 也重写了它——那么三张虚方法表的索引 0 都指向 makeSound,但实际存储的地址分别是 Animal、Dog、Cat 类中该方法的字节码入口。
方法调用时 JVM 怎么查表?
当你写 animal.makeSound(),JVM 不看变量声明类型 Animal,而是看运行时对象的真实类型(比如其实是 Dog 实例)。然后:
- 找到该对象所属类(
Dog.class)的虚方法表 - 根据方法签名(如
makeSound()V)定位到对应槽位(比如索引 0) - 取出该槽位存储的方法地址,跳转执行
这个过程叫「动态绑定」或「晚期绑定」,全程在运行时完成,不需要遍历继承链,效率很高。
为什么 static / private / final 方法不进虚方法表?
因为它们都不需要动态分派:
-
static:调用目标由编译期变量类型决定,比如
Animal.work()永远调父类的,跟 new 出什么对象无关 - private:仅限本类访问,子类根本看不到,更谈不上重写或继承调用
- final:明确禁止重写,JVM 可以安全地做内联优化,连查表都省了
它们虽然可能被子类“看到”(如 static 方法可被继承),但不属于虚方法体系,也不参与多态。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











