Адрес a.print()
Отпечатки 4
по причине polymorphism. Вызываемый метод зависит от типа времени выполнения a
, который равен B
. Неважно, когда это называется; полиморфизм применяется всегда.
Оба раза, метод называется. Один раз из конструктора A
, который вызывается конструктором по умолчанию в B
. В другой раз ваш явный звонок в main
.
Причина, по которой первые печатные выходы 0
, а не 4
происходит потому, что в то время, что называется print
, A
все еще строится. То есть, конструктор A
все еще выполняется. Прежде чем конструктор суперкласса вернется, в подклассе еще ничего не инициализируется, даже переменные инициализаторы. Значение 4
назначается после завершения конструктора суперкласса, но до того, как завершится работа над конструктором подкласса. Поскольку инициализаторы переменных еще не запущены, значение по умолчанию 0
(это будет false
для boolean
и null
для объектов) - это значение i
в первой печати.
Этот порядок перечисленное JLS, Section 12.5:
Непосредственно перед ссылкой на вновь созданный объект возвращается в качестве результата, указанный конструктор обрабатывается для инициализации нового объекта, используя следующую процедуру:
Назначьте аргументы для конструктора вновь созданным переменным параметра для этого вызова конструктора.
Если этот конструктор начинается с явного вызова конструктора (§8.8.7.1) другого конструктора в том же классе (с использованием этого), затем оценивайте аргументы и обрабатывайте вызов конструктора рекурсивно, используя эти пять шагов. Если вызов конструктора завершается внезапно, то эта процедура завершается внезапно по той же причине; в противном случае перейдите к шагу 5.
Этот конструктор не начинается с явного вызова конструктора другого конструктора в том же классе (с использованием этого). Если этот конструктор относится к классу, отличному от Object, то этот конструктор начнет с явного или неявного вызова конструктора суперкласса (используя super). Оцените аргументы и обработайте вызов конструктора суперкласса рекурсивно, используя эти пять шагов. Если вызов конструктора завершается внезапно, то эта процедура завершается внезапно по той же причине. В противном случае перейдите к шагу 4.
выполнять экземпляр инициализаторы и переменные экземпляр инициализаторы для этого класса, присваивая значения переменного экземпляра инициализаторов в соответствующем переменный экземпляре, в слева-направо порядке, в котором они появляются текстуальны в исходном коде для класс. Если выполнение любого из этих инициализаторов приводит к исключению, то никакие новые инициализаторы не обрабатываются, и эта процедура завершается внезапно с тем же исключением. В противном случае перейдите к шагу 5.
Выполнение остальной части тела этого конструктора. Если это выполнение завершается внезапно, то эта процедура завершается внезапно по той же причине. В противном случае эта процедура завершится нормально.
(жирный курсив мой)
Это пример того, почему это плохая идея, чтобы вызвать метод, который может быть переопределен из конструктора. Состояние подкласса еще не инициализировано.
'a.print' не ссылается на класс A, a является просто объектом типа' A' со ссылкой на тип 'B'. – Geinmachi
Это отличный пример того, почему мы должны избегать полиморфных методов внутри конструкторов. Чтобы избежать таких проблем, используйте только те методы, которые являются частными, статическими или окончательными. – Pshemo