Ответ 1
Каждый класс 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() { }
}
приводит к таблицам
BaseClass 1. BaseClass/method1() 2. BaseClass/method2() 3. BaseClass/method3() NextClass 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() { }
}
Эта иерархия классов приводит к таблицам виртуальных методов
AnotherClass 1. BaseClass/method1() 2. NextClass/method2() 3. BaseClass/method3() 4. AnotherClass/method4() 5. MyInterface/ifaceMethod() MyClass 1. MyClass/method5() 2. MyInterface/ifaceMethod()
Как вы можете видеть, AnotherClass
содержит метод интерфейса в своей пятой записи, а MyClass
содержит его во второй записи. Чтобы действительно найти правильную запись в таблице виртуальных методов, вызов метода с invokeinterface
всегда будет искать полную таблицу без возможности для стиля оптимизации, который делает invokevirtual
.
Существуют дополнительные отличия, такие как факт, что invokeinterface
может использоваться вместе с объектными ссылками, которые фактически не реализуют интерфейс. Следовательно, invokeinterface
будет проверять во время выполнения, существует ли метод в таблице и потенциально генерировать исключение. Если вы хотите глубже погрузиться в эту тему, я предлагаю, например, "Эффективная реализация интерфейсов Java: Invokeinterface считается безвредной" .