Java多态的动态派发本质依赖JVM在类加载时构建的虚方法表(vtable),由invokevirtual指令结合对象实际类型与方法签名在vtable中查找具体实现。
Java多态的动态派发本质依赖虚方法表(vtable)机制,而字节码层面并不直接暴露vtable结构——它由JVM在类加载阶段构建并隐式使用。真正决定调用哪个方法的,是字节码中的invokevirtual指令与运行时对象的实际类型、方法签名及vtable索引的协同工作。
invokevirtual 是Java中调用实例方法(非private、非static、非final)的标准字节码指令。它不硬编码目标方法地址,而是根据栈顶对象的实际类型,在其类的虚方法表中查找匹配的方法入口。
Ljava/lang/Object;.toString:()Ljava/lang/String;)你无法在.class文件里看到vtable数组——它是JVM在类加载的“连接”阶段(特别是准备和解析阶段)为每个类生成的内部数据结构。每个类(除final类外)都有自己的vtable,内容包括:
用javap -v查看编译后的字节码,重点关注invokevirtual指令的操作数,它指向常量池中一个Methodref。这个符号引用看似指向父类声明的方法,但实际运行时JVM会基于对象真实类型查vtable:
立即学习“Java免费学习笔记(深入)”;
Animal a = new Dog(); a.speak();,字节码中仍是invokevirtual Animal.speak
a实际是Dog实例,于是查Dog类的vtable,找到Dog.speak的入口Dog没重写speak,则vtable中该槽位仍指向Animal.speak
这些方法在编译期就能确定唯一目标,因此使用更轻量的指令,不参与动态派发:
invokestatic:调用静态方法,直接绑定到类符号引用invokespecial:调用私有方法、构造器、父类方法(如super.xxx()),地址在编译期解析完成invokeinterface:接口方法调用,使用独立的itable(接口方法表),原理类似vtable但支持多实现invokevirtual调用,但JIT可能内联优化,跳过vtable查表理解vtable的关键不是在字节码里“看见”它,而是明白invokevirtual如何触发JVM的运行时查表逻辑——这正是Java多态能“以父类引用来调用子类实现”的底层支撑。