it-roy-ru.com

Инициализированы ли поля перед выполнением кода конструктора в 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();
    }
}
57
Praveen Kumar

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

  1. Инициализаторы статических переменных и блоки статической инициализации в текстовом порядке, если класс не был предварительно инициализирован.
  2. Вызов super () в конструкторе, будь то явный или неявный.
  3. Инициализаторы переменных экземпляра и блоки инициализации экземпляра в текстовом порядке.
  4. Оставшееся тело конструктора после super ().

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

83
user207421

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

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. Первая строка конструктора Xnew Y() вызывается.
  4. Печать Y
  5. Печать X
  6. Вызов первой строки в конструкторе Z new Y()
  7. Печать Y
  8. Распечатать Z

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

53
Arun P Johny

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

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");
        }
    }
}

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

1
Vivek

Чтобы прояснить заблуждения со статическими - я просто буду ссылаться на этот небольшой кусок кода:

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"); }
  static public void main(String p[]) {
    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://stackoverflow.com/a/30837385/744133 .

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

  1. В первый раз, когда объект инициализируется, Инициализирует текущий объект как для статической инициализации, так и для инициализации экземпляра, смешанной в зависимости от порядка появления

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

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

1
YoYo