每个 Java 类都与一个虚拟方法表相关联,其中包含指向类的每个方法的字节码的“链接”。该表继承自特定类的超类,并针对子类的新方法进行了扩展。例如,
class BaseClass {
public void method1() { }
public void method2() { }
public void method3() { }
}
class NextClass extends BaseClass {
public void method2() { } // overridden from BaseClass
public void method4() { }
}
表格中的结果
基类
1. BaseClass/method1()
2. BaseClass/method2()
3. BaseClass/method3()
下一班
1. BaseClass/method1()
2. NextClass/method2()
3. BaseClass/method3()
4. NextClass/method4()
注意,NextClass 的虚方法表如何保留BaseClass 表的条目顺序,并仅覆盖它所覆盖的method2() 的“链接”。
JVM 的实现因此可以优化对invokevirtual 的调用,方法是记住BaseClass/method3() 将始终是该方法将被调用的任何对象的虚拟方法表中的第三个条目。
对于invokeinterface,这种优化是不可能的。例如,
interface MyInterface {
void ifaceMethod();
}
class AnotherClass extends NextClass implements MyInterface {
public void method4() { } // overridden from NextClass
public void ifaceMethod() { }
}
class MyClass implements MyInterface {
public void method5() { }
public void ifaceMethod() { }
}
这个类层次结构产生虚方法表
另一个类
1. BaseClass/method1()
2. NextClass/method2()
3. BaseClass/method3()
4. 另一个类/方法4()
5.MyInterface/ifaceMethod()
我的课
1. MyClass/method5()
2.MyInterface/ifaceMethod()
如您所见,AnotherClass 在其第五个条目中包含接口的方法,MyClass 在其第二个条目中包含它。要真正在虚方法表中找到正确的条目,使用invokeinterface 调用方法总是必须搜索整个表,而没有机会使用invokevirtual 所做的优化风格。
还有一些不同之处,例如invokeinterface 可以与实际上不实现接口的对象引用一起使用。因此,invokeinterface 必须在运行时检查表中是否存在方法并可能引发异常。