java多态底层原理是虚方法表(vtable)实现的动态单分派:jvm在类加载准备阶段为每个非final类生成静态只读vtable,存储可重写实例方法的入口地址;对象通过vptr指针访问所属类vtable,调用时根据编译期确定的索引o(1)查表跳转,仅依据接收者实际类型完成一次分派。

Java 中多态的底层原理,核心就是虚方法表(vtable)这张“静态跳转表”。它不是运行时临时拼凑的,而是在类加载的准备和解析阶段,由 JVM 为每个非 final 类提前建好的一张只读函数指针数组。
vtable 是类级别的共享结构
每个类对应一张 vtable,所有该类的实例对象都共享同一张表。对象本身并不存整张表,而是在对象头里隐式带一个指向其所属类 vtable 的指针(vptr)。这个指针很小——64 位系统仅占 8 字节,是实现多态唯一额外的空间成本。
- vtable 存在 Metaspace 中,随 Class 对象常驻内存,类不卸载,表就不释放
- 表结构在类首次被主动使用前就完全固定,后续不会扩容、不会重建
- 子类 vtable 并非全新生成,而是以父类 vtable 为基础复制并覆盖/追加
槽位顺序由源码声明顺序决定
vtable 中每个槽位对应一个可被重写的方法(即非 private、非 static、非 final 的实例方法),且槽位索引在编译期就确定了。比如 Object.toString() 在所有类的 vtable 中始终位于同一偏移位置(如第 2 号槽)。
- 父类方法未被重写 → 子类 vtable 对应槽位直接填父类方法地址
- 父类方法被子类重写 → 同一槽位被替换成子类方法入口地址
- 子类新增 public 方法 → 追加到表尾新槽位,不影响已有方法的索引
调用过程是 O(1) 的精准查表
执行 obj.sound() 时,字节码指令 invokevirtual 触发三步操作:
- 取 obj 引用,检查是否为 null(为空则抛 NullPointerException)
- 通过对象头拿到实际类型(如 Dog.class),再从中取出 vtable 地址
- 根据方法符号引用查常量池,算出它在 vtable 中的固定索引,直接取指针并跳转
整个过程没有字符串匹配、没有继承链遍历、没有哈希计算,纯靠内存偏移寻址,极快且稳定。
哪些方法不进 vtable?
vtable 只服务于动态绑定,以下方法绕过它,走静态指令:
- private 方法:仅本类可见,子类同名方法是独立符号,无覆盖关系
- static 方法:属于类而非实例,调用目标由字节码符号引用锁定
- final 方法:禁止重写,JVM 直接内联或生成 invokespecial 指令
- 构造器:本质是初始化逻辑,不参与多态分派
接口调用走的是另一套机制 —— itable,按接口维度组织,与 vtable 并行存在、互不干扰。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











