Java重写方法的动态分派依赖invokevirtual指令和类加载时构建的vtable:JVM通过对象头klass指针获取实际类型,再查该类vtable固定槽位跳转;static/private/final方法绕过vtable,用invokestatic或invokespecial静态绑定。

Java 重写方法的动态分派,不是运行时“现场查找”,而是靠 JVM 在类加载阶段就建好结构、运行时快速查表完成的。核心落地点有两个:字节码指令 invokevirtual 和每个类对应的 虚方法表(vtable)。
invokevirtual 指令是动态分派的触发开关
所有非 static、非 private、非 final 的实例方法调用,在编译后都生成 invokevirtual 指令。它不硬编码目标方法地址,只记录方法符号引用。JVM 执行时才做两件事:
- 从操作数栈顶取出对象引用,读取其对象头中的 klass 指针,拿到该对象的真实运行时类型(比如 Sub.class);
- 根据方法签名(名称 + 描述符),去该类型的 vtable 中定位对应槽位,跳转到槽位指向的具体字节码入口。
vtable 是动态分派的执行依据
vtable 是 JVM 在类加载的准备阶段之后、初始化之前为每个类(接口除外)构建的一张函数指针数组。它的关键特性包括:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 父类中可被重写的方法(public/protected/default 实例方法)在 vtable 中有固定槽位;
- 子类继承父类 vtable 结构,若重写了某个方法,就直接把对应槽位的指针替换成子类方法的入口地址;
- 哪怕对象还没构造完(如父类构造器中调用 this.m()),klass 指针已指向子类,vtable 已就绪,所以仍能准确调到 Sub.m() —— 这就是“类型恒定、状态未就绪”的根源。
动态分派不依赖对象状态,只依赖类型元数据
整个过程完全基于类的元信息,和字段是否初始化、构造器是否执行完毕无关:
立即学习“Java免费学习笔记(深入)”;
- 类加载验证阶段已校验重写合法性(签名一致、访问权限不更严、返回类型协变等),违规则抛 VerifyError;
- 接口方法调用用 invokeinterface,虽需扫描匹配,但原理一致:按实际类型找其实现类的 vtable;
- 声明类型(如 Animal a = new Dog())仅影响编译期检查,对 invokevirtual 查 vtable 无任何作用——JVM 只认 new Dog() 这个对象头里的 klass 指针。
哪些情况会绕过动态分派
不是所有方法调用都走 vtable 查找:
- static 方法:用 invokestatic,绑定到声明类型,与对象实际类型无关;
- private / final / 构造器:用 invokespecial,编译期就解析为直接引用,不进 vtable;
- 字段访问:不具多态性,a.field 永远取声明类型 A 中定义的字段值;
- 重载方法:由编译器按参数静态类型选方法签名,生成不同指令,与 vtable 无关。

















