В вашем примере A a = new B()
, a
- это полиморфная ссылка - ссылка, которая может указывать на другой объект из иерархии классов (в данном случае это ссылка на объект типа B
но может также использоваться как ссылка на объект класса A
, который является самым верхним в иерархии объектов).
Что касается конкретного поведения, о котором вы спрашиваете:
Почему B
напечатано в выводе?
Какой конкретный метод show(B obj)
будет вызываться через переменную-ссылку, зависит от ссылки на объект, который он содержит в определенный момент времени.То есть: если он содержит ссылку на объект класса B
, будет вызван метод из этого класса (это ваш случай), но если он будет указывать на объект класса A
, ссылка на этот объект будетназывается.Это объясняет, почему B
печатается в выходных данных.
иерархия).
Почему and A
печатается в выходных данных?
Метод в подклассе с тем жеимя, но другая подпись называется перегрузка метода .Он использует статическое связывание, что означает, что соответствующий метод будет привязан во время компиляции .Компилятор не имеет ни малейшего представления о типе ваших объектов runtime .
Так что show(A obj)
класса A
будет привязано в этом случае.Однако когда метод будет фактически вызван в runtime , будет вызвана его реализация из класса B
(show(A obj)
из класса B
), и поэтому вы видите B and A
, а не A and A
в выводе.
Ссылка для invokevirutal
(инструкция JVM, вызываемая при выполнении виртуальных методов) :
Если разрешеноМетод не является сигнатурным полиморфным (§2.9), тогда инструкция invokevirtual выполняется следующим образом.
Пусть C - класс objectref.Фактический метод, который должен быть вызван, выбирается следующей процедурой поиска:
Если C содержит объявление для метода экземпляра m, который переопределяет (§5.4.5) разрешенный метод, то m - это метод, который должен быть вызвани процедура поиска завершается.
В противном случае, если C имеет суперкласс, эта же процедура поиска выполняется рекурсивно с использованием прямого суперкласса C;метод, который должен быть вызван, является результатом рекурсивного вызова этой процедуры поиска.
В противном случае вызывается AbstractMethodError.
Для a.show(c)
то же самоеправила применяются так же, как и для B
, поскольку C
не имеет перегруженных методов и напрямую наследуется от B
.
РЕДАКТИРОВАТЬ:
Шаг за шагомпошаговое объяснение того, почему a.show(c)
печатает B and A
:
- Компилятор распознает объект
a
как objectref
для объекта класса A
(время компиляции) - Поскольку
a
имеет тип A
, метод A::show(A obj)
связан. - Когда код фактически выполняется (т. Е. Метод
show()
вызывается для объекта a
), среда выполнения распознает эту ссылку a
полиморфно указывает на объект типа B
(это из-за A a = new B()
) (время выполнения) - Поскольку
C extends B
, среда выполнения обрабатывает a.show(c)
так же, как и b.show(c)
(или b.show(b)
)), поэтому B::show(A obj)
используется в этом случае, но вместо obj
используется объект типа B
.Вот почему печатается буква «B и A».