Вопрос-ответ

Are fields initialized before constructor code is run in Java?

Инициализируются ли поля перед запуском кода конструктора в Java?

Кто-нибудь может объяснить результат следующей программы? Я думал, конструкторы инициализируются перед переменными экземпляра. Поэтому я ожидал, что результат будет "XZYY".

class X {
Y b = new Y();

X() {
System.out.print("X");
}
}

class Y {
Y() {
System.out.print("Y");
}
}

public class Z extends X {
Y y = new Y();

Z() {
System.out.print("Z");
}

public static void main(String[] args) {
new Z();
}
}
Переведено автоматически
Ответ 1

Правильный порядок инициализации таков:


  1. Инициализаторы статических переменных и блоки статической инициализации в текстовом порядке, если класс не был инициализирован ранее.

  2. Вызов super() в конструкторе, явный или неявный.

  3. Инициализаторы переменных экземпляра и блоки инициализации экземпляра в текстовом порядке.

  4. Оставшееся тело конструктора после super().

Смотрите разделы §2.17.5-6 спецификации виртуальной машины Java.

Ответ 2

Если вы посмотрите на декомпилированную версию файла класса

class X {
Y b;

X() {
b = new Y();
System.out.print("X");
}
}

class Y {
Y() {
System.out.print("Y");
}
}

public class Z extends X {

Y y;

Z() {
y = new Y();
System.out.print("Z");
}

public static void main(String args[]) {
new Z();
}
}

Вы можете обнаружить, что переменная экземпляра y перемещается внутри конструктора, поэтому последовательность выполнения следующая


  1. Вызовите конструктор Z

  2. Это запускает конструктор по умолчанию для X

  3. Вызывается первая строка X конструктора new Y().

  4. Напечатать Y

  5. Печать X

  6. Вызовите первую строку в constructor Z new Y()

  7. С принтами Y

  8. Печать Z

Все переменные экземпляра инициализируются с помощью инструкций конструктора.

Ответ 3

Когда вы вызываете конструктор, инициализаторы переменных экземпляра запускаются перед телом конструктора. Как вы думаете, какой результат дает приведенная ниже программа?

public class Tester {
private Tester internalInstance = new Tester();
public Tester() throws Exception {
throw new Exception("Boom");
}
public static void main(String[] args) {
try {
Tester b = new Tester();
System.out.println("Eye-Opener!");
} catch (Exception ex) {
System.out.println("Exception catched");
}
}
}

Метод main вызывает конструктор Tester, который выдает исключение. Можно ожидать, что предложение catch перехватит это исключение и выведет перехваченное исключение.
Но если вы попытались запустить его, вы
обнаружили, что он ничего подобного не делает, и выдает StackOverflowError.

Ответ 4

Чтобы прояснить неправильные представления о static, я просто обращусь к этому небольшому фрагменту кода:

public class Foo {

{
System.out.println("Instance Block 1");
}

static {
System.out.println("Static Block 1");
}

public static final Foo FOO = new Foo();

{
System.out.println("Instance Block 2");
}

static {
System.out.println("Static Block 2 (Weird!!)");
}

public Foo() {
System.out.println("Constructor");
}

public static void main(String[] args) {
System.out.println("In Main");
new Foo();
}
}

Сюрприз заключается в том, что результат выглядит следующим образом:

Static Block 1
Instance Block 1
Instance Block 2
Constructor
Static Block 2 (Weird!!)
In Main
Instance Block 1
Instance Block 2
Constructor

Обратите внимание, что у нас есть static {} который вызывается после двух экземпляров {}. это происходит потому, что мы запускаем конструктор в середине, меняя порядок выполнения при первом вызове конструктора.

Обнаружил это, когда работал над этим ответом - https://javalang.ru/a/30837385/744133.

В основном мы наблюдаем, как это происходит:


  1. При первой инициализации объекта инициализируйте текущий объект как для статической, так и для инициализации экземпляра, чередуя их в зависимости от порядка появления



  2. Для всех следующих инициализаций выполняйте инициализацию экземпляра только в порядке появления, поскольку статическая инициализация уже произошла.



Мне нужно исследовать, как сочетание наследования и явных и неявных вызовов super повлияет на это, и я обновлю результаты. Скорее всего, это будет похоже на другие предоставленные ответы, за исключением того, что они ошиблись со статической инициализацией.

java