每个Java类都与一个虚拟方法表相关联,该表包含指向该类每个方法的字节码的“链接”。该表是从特定类的超类继承的,并针对子类的新方法进行了扩展。例如,
class BaseClass {
public void method1() { }
public void method2() { }
public void method3() { }
}
class NextClass extends BaseClass {
public void method2() { }
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() { }
public void ifaceMethod() { }
}
class MyClass implements MyInterface {
public void method5() { }
public void ifaceMethod() { }
}
该类层次结构产生虚拟方法表
另一个类
1. BaseClass / method1()
2. NextClass / method2()
3. BaseClass / method3()
4. AnotherClass / method4()
5. MyInterface / ifaceMethod()
我的课
1. MyClass / method5()
2. MyInterface / ifaceMethod()
如您所见,AnotherClass
该接口的方法在其第五项中MyClass
包含,而在其第二项中其包含。为了在虚拟方法表中实际找到正确的条目,使用方法进行的调用invokeinterface
将始终必须搜索整个表,而没有机会进行优化invokevirtual
。
还有其他一些差异,例如事实,invokeinterface
可以与实际上未实现接口的对象引用一起使用。因此,invokeinterface
将必须在运行时检查表中是否存在方法并可能引发异常。