Java运行时多态的核心是JVM动态分派机制,通过虚方法表(vtable)实现:父类与子类同签名方法在vtable中索引一致,运行时依对象实际类型查对应vtable相同索引位置的方法地址;重载属静态分派,编译期按参数静态类型绑定,不涉vtable。

Java 中方法重写实现运行时多态,核心靠的是 JVM 的动态分派(Dynamic Dispatch)机制,而不是编译器“记住”谁重写了谁。你写的 Animal a = new Dog() 这行代码本身不触发任何特殊逻辑,真正起作用的是在运行时,JVM 根据对象的实际类型去查找并调用对应的方法实现。
动态分派怎么找对方法?靠虚方法表(vtable)
JVM 为每个类维护一张虚方法表,表中按固定顺序存放所有可被重写(非 static、final、private)的实例方法的入口地址。关键点在于:
- 父类和子类中签名相同的方法,在各自 vtable 中占据相同索引位置
- 当执行
a.speak()时,JVM 先通过引用a的静态类型(Animal)查它的 vtable,拿到speak()方法的索引号(比如是第 3 位) - 再根据
a实际指向的对象(new Dog()),取出Dog类的 vtable,直接访问第 3 位——那里存的就是Dog.speak()的字节码地址
为什么重载不算运行时多态?它走的是静态分派
重载(Overload)在编译期就决定了调用哪个方法,依据是参数的静态类型。比如:
void speak(Animal a) { ... }
void speak(Dog d) { ... }
speak(new Dog()); // 编译器看到参数是 Dog 类型,直接绑定到第二个方法
这个过程不查对象实际类型,也不用 vtable,完全由 javac 在生成字节码时写死符号引用。所以重载属于编译时多态,和动态分派无关。
立即学习“Java免费学习笔记(深入)”;
哪些方法无法参与动态分派?
以下方法调用在编译期就绑定好了,绕过 vtable 查找:
- static 方法:属于类,不依赖对象实例
- private 方法:无法被继承,更谈不上重写
- final 方法:明确禁止重写,JVM 可直接内联优化
- 构造方法:不是普通成员方法,没有多态语义
接口方法调用稍有不同:用 invokeinterface 指令
当使用接口引用(如 USB device = new Mouse())调用方法时,JVM 不能像类继承那样靠固定偏移查表,因为一个类可能实现多个接口,方法在各接口 vtable 中位置不统一。此时 JVM 会扫描目标类及其所有实现接口的方法表来匹配签名,效率略低于 invokevirtual,但语义一致——仍是运行时根据实际对象类型决定行为。


















