#java #oop #inheritance
#java #ооп #наследование
Вопрос:
Рассмотрим следующие классы:
public class A {
String bar = "A.bar";
A() { foo(); }
public void foo() {
System.out.println("A.foo(): bar = "
bar);
}
}
public class B extends A {
String bar = "B.bar";
B() { foo(); }
public void foo() {
System.out.println("B.foo(): bar = "
bar);
}
}
public class C {
public static void main(String[]
args) {
A a = new B();
System.out.println("a.bar = "
a.bar);
a.foo();
}
}
Теперь напечатана первая строка: B.foo(): bar = null
. Насколько я понимаю, инициализация элементов предшествует конструктору, но я вижу, что здесь это не так.
Почему на этом этапе B#bar
не инициализируется?
Когда B#bar
инициализируется?
Спасибо!
Обновить:
После прочтения ваших ответов, пожалуйста, взгляните на этот контрпример:
public class Bazz {
public int a = 42;
public Bazz() {
System.out.println(a);
}
public static void main(String[] args) {
Bazz bazz = new Bazz();
}
}
В данном случае a
это не 0
так. Вместо этого конструктор Bazz()
печатает 42
.
В чем разница?
Комментарии:
1. Метод переопределен, но вы объявляете новую отдельную переменную.
Ответ №1:
Позвольте мне перечислить шаги:
A a = new B();
// Он видит, что B является дочерним элементом, поэтому вызывает конструктор A (B.bar еще не инициализирован)- Вызывается конструктор A
foo
. Поскольку он был переопределен B, вызывается Bfoo
и отображает значение null. - Инициализируется после завершения работы конструктора A
B.bar
. - Затем вызывается конструктор B.
Примечание: даже если super()
не вызывается в B, компилятор автоматически добавляет этот оператор.
РЕДАКТИРОВАТЬ: Как компилятор видит конструктор B:
super();
this.bar = «B.bar»;
foo();
ОБНОВЛЕНИЕ: ваш новый результат вытекает из того же объяснения, которое я дал ранее:
-
Bazz bazz = new Bazz();
// При вызове конструктора переменная a инициализируется значением 42 -
System.out.println(a);
// выводит 42
Переменные экземпляра инициализируются до выполнения конструктора, но после выполнения конструктора super. Обратитесь к моей правке выше.
Комментарии:
1. Теперь я понял. (1) конструктор супер (2) переменных (3). Спасибо!
2. @user3307716 О, ты мог бы завернуть
Bazz bazz = new Bazz();
иSystem.out.println(a);
в код.
Ответ №2:
Вы создаете через new B()
, и в конструкторе B()
он вызывает super()
.
Таким образом, ваш код вызывает A a = new B();
, который попадает в System.out.println("B.foo(): bar = " bar);
. На данный момент a
сборка не завершена (не полностью инициализирована), поэтому B.bar
есть null
. bar
будет инициализировано с помощью = "B.bar"
, когда конструктор нажмет step-return at }
.
TL; DR:
Почему на этом этапе
B#bar
не инициализируется?
Потому что a
не полностью инициализирован в System.out.println("B.foo(): bar = " bar);
.
Когда
B#bar
инициализируется?
}
(Завершение конструктора).
Для записи, вот стандартный журнал:
B.foo(): bar = null // bar не был инициализирован. Мы находимся на:
A() { foo(); }
B.foo(): bar = B.bar // был инициализирован bar. Мы находимся на:
B() { foo(); }
a.bar = A.bar // Вы вызвали «println a.bar =
a.bar
«. ВA a
,bar
равно.bar.B.foo(): bar = B.bar // foo() полиморфен и, поскольку
a
являетсяclass B
,B.foo()
был вызван, отсюда и эта строка.
@edit: Bazz.a
имеет тип int
. int
никогда не будет null
, и компилятор автоматически принимает такие переменные для замены ваших операторов при передаче компилятору. Так System.out.println(a);
становится System.out.println(42);
.
Ответ №3:
При создании объекта из класса первым запускается статический инициализатор.
Он инициализирует статические атрибуты и статические методы,
Затем запускается инициализатор объекта,
Сначала запускается конструктор объекта B()
,
таким образом, нестатические атрибуты ( bar
) инициализируются позже. таким образом, атрибуты инициализируются после выполнения конструктора. Итак, bar — это null
Если вы объявили bar как статический,
static String bar = "B.bar";
Вы можете сначала инициализировать переменную bar;
вывод будет;
B.foo(): bar = B.bar
B.foo(): bar = B.bar
a.bar = A.bar
B.foo(): bar = B.bar
Ответ №4:
Порядок такой:
- Инициализация
A.bar
- Выполнение
A
конструктора - Инициализация
B.bar
- Выполнение
B
конструктора
Обратите внимание, что bar
in A
— это переменная, отличная от bar
in B
. Поскольку конструктор A
вызывает переопределенный метод, этот метод может получить доступ к переменной экземпляра в B
до ее инициализации.