在Java虚拟机(JVM)中,invokevirtual 是一个字节码指令,用于调用对象实例的方法。它的主要作用是执行对象的虚方法调用,遵循Java语言中的动态分派机制。
例如,假设我们有以下Java代码:
Java
1public class Animal {
2    public void makeSound() {
3        System.out.println("Animal makes a sound");
4    }
5}
6
7public class Dog extends Animal {
8    @Override
9    public void makeSound() {
10        System.out.println("Dog barks");
11    }
12
13    public static void main(String[] args) {
14        Animal myPet = new Dog();
15        myPet.makeSound(); // 这行代码编译后将包含invokevirtual指令
16    }
17}
当JVM执行到 myPet.makeSound() 时,对应的字节码会包含 invokevirtual 指令,它的工作原理如下:
- 首先,根据栈顶的对象引用找到实际的对象类型。在这个例子中,虽然变量 myPet的静态类型是Animal,但其实际类型是Dog。
- JVM查找实际类型 Dog中名为makeSound的方法签名,并执行该方法体。
- 如果没有在实际类型中找到对应的方法,则按照继承关系向上查找父类直到找到合适的实现或者到达顶级父类(Object),如果没有找到则抛出 AbstractMethodError或者NoSuchMethodError异常。
因此,在上述示例中,尽管通过 Animal 类型的引用调用了 makeSound 方法,但由于运行时的实际对象是 Dog 类型,所以最终执行的是 Dog 类中重写的 makeSound 方法,输出 “Dog barks”。这就是 invokevirtual 指令如何实现在运行时基于对象实际类型的多态性方法调用。