В принципе любой метод, который не объявлен final
, может быть переопределен в любом наследующем классе. Стратегия разрешения виртуальной таблицы - это то, как Java динамически отправляет вызовы метода в определение метода типа времени выполнения объекта. Например, если у вас есть объект s
, который объявлен как тип Shape
, но был создан экземпляр new Circle()
(то есть Shape s = new Circle()
), а затем вы вызываете s.draw()
... если Circle переопределяет draw
Вы хотите, чтобы вызывалась версия метода рисования в Circle, а не версия этого метода в Shape. Эта информация может быть получена только во время выполнения (если вы передаете объект Shape, он может быть передан из JAR, который компилятор никогда не увидит, поэтому у компилятора нет способа выяснить, какой конкретный подкласс Shape был создан), и поэтому виртуальная машина должна направить вызов метода в правильное определение метода (в данном случае версия рисования, предоставленная Circle).
// Shape.java
public class Shape
{
public void draw(){
System.out.println("I'm a Shape");
}
}
// Circle.java
public class Circle extends Shape
{
public void draw(){
System.out.println("I'm a Circle");
}
}
// Elsewhere
Shape s = new Circle();
s.draw(); // should print "I'm a Circle"
В большинстве реализаций виртуальной таблицы каждый класс имеет таблицу, в которой каждая запись является функцией-членом и соответствующим адресом. Таким образом, виртуальная таблица для Circle и Shape будет иметь запись в таблице для «рисования», но виртуальная таблица для Circle и Shape имеет запись для точки отрисовки для соответствующих определений этой функции. Затем каждый экземпляр класса указывает на виртуальную таблицу данного класса. Итак, когда вы делаете new Circle()
, он содержит запись, указывающую на виртуальную таблицу Circle. Когда вызывается метод, который не объявлен final
, соответствующие реализации метода вызываются путем поиска соответствующего смещения в виртуальной таблице объекта.