Все дело в производительности. Когда, вычисляя тип времени компиляции (aka: статический тип), JVM может вычислить индекс вызванного метода в таблице виртуальных функций типа времени выполнения (aka: динамический тип). Используя этот индекс, шаг 3 просто становится доступом к массиву, который может выполняться за постоянное время. Зацикливание не требуется.
Пример:
class A {
void foo() { }
void bar() { }
}
class B extends A {
void foo() { } // Overrides A.foo()
}
По умолчанию A
расширяет Object
, который определяет эти методы (конечные методы опускаются, так как они вызываются через invokespecial
):
class Object {
public int hashCode() { ... }
public boolean equals(Object o) { ... }
public String toString() { ... }
protected void finalize() { ... }
protected Object clone() { ... }
}
Теперь рассмотрим этот вызов:
A x = ...;
x.foo();
Выяснив, что статический тип x равен A
, JVM также может выяснить список методов, доступных на этом сайте вызова: hashCode
, equals
, toString
, finalize
, clone
, foo
, bar
. В этом списке foo
- 6-я запись (hashCode
- 1-я, equals
- 2-я и т. Д.). Это вычисление индекса выполняется один раз - когда JVM загружает файл класса.
После этого всякий раз, когда JVM обрабатывает x.foo()
, просто необходим доступ к 6-й записи в списке методов, предлагаемых x, что эквивалентно x.getClass().getMethods[5]
(что указывает на A.foo()
, если динамический тип x равен A
) и вызвать этот метод. Нет необходимости исчерпывающе искать в этом массиве методов.
Обратите внимание, что индекс метода остается неизменным независимо от динамического типа x. То есть: даже если x указывает на экземпляр B, шестой метод по-прежнему foo
(хотя на этот раз он будет указывать на B.foo()
).
Обновление
[В свете вашего обновления]: Вы правы. Для выполнения виртуальной диспетчеризации метода все, что нужно JVM - это имя + подпись метода (или смещение в виртуальной таблице). Однако JVM не выполняет вещи вслепую. Сначала он проверяет правильность загруженных в него касс-файлов в процессе, называемом проверка (см. Также здесь ).
Подтверждение выражает один из принципов разработки JVM: Он не зависит от компилятора для получения правильного кода . Он проверяет сам код перед тем, как разрешить его выполнение. В частности, верификатор проверяет, что каждый вызванный виртуальный метод фактически определяется статическим типом объекта-получателя. Очевидно, что для выполнения такой проверки необходим статический тип приемника.