В каком порядке запускаются статические / экземплярные инициализаторы в Java? - PullRequest
88 голосов
/ 05 января 2010

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

package pkg;

public class LoadTest {
    public static void main(String[] args) {
        System.out.println("START");
        new Child();
        System.out.println("END");
    }
}

class Parent extends Grandparent {
    // Instance init block
    {
        System.out.println("instance - parent");
    }

    // Constructor
    public Parent() {
        System.out.println("constructor - parent");
    }

    // Static init block
    static {
        System.out.println("static - parent");
    }
}

class Grandparent {
    // Static init block
    static {
        System.out.println("static - grandparent");
    }

    // Instance init block
    {
        System.out.println("instance - grandparent");
    }

    // Constructor
    public Grandparent() {
        System.out.println("constructor - grandparent");
    }
}

class Child extends Parent {
    // Constructor
    public Child() {
        System.out.println("constructor - child");
    }

    // Static init block
    static {
        System.out.println("static - child");
    }

    // Instance init block
    {
        System.out.println("instance - child");
    }
}

и получил этот вывод:

START
статический - дедушка
статический - родительский
статический - дочерний
экземпляр - дедушка
конструктор - дедушка
экземпляр - родитель
конструктор - родитель
экземпляр - ребенок
конструктор - ребенок
END

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

РЕДАКТИРОВАТЬ:

Я изменилмой пример кода, добавив его в LoadTest.java:

class IAmAClassThatIsNeverUsed {
    // Constructor
    public IAmAClassThatIsNeverUsed() {
        System.out.println("constructor - IAACTINU");
    }

    // Instance init block
    {
        System.out.println("instance - IAACTINU");
    }

    // Static init block
    static {
        System.out.println("static - IAACTINU");
    }
}

Как следует из имени класса, я нигде не ссылался на новый класс.Новая программа выдала тот же вывод, что и старая.

Ответы [ 8 ]

90 голосов
/ 05 января 2010

См. Разделы 12.4 и 12.5 JLS версии 8 , они подробно рассказывают обо всем этом (12.4 для статических и 12,5 для переменных экземпляра).

Для статической инициализации (раздел 12.4):

Класс T или интерфейс типа T будут инициализированы непосредственно перед первым возникновением любого из следующих действий:

  • T является классом, и создается экземпляр T.
  • T является классом, и вызывается статический метод, объявленный T.
  • Назначено статическое поле, объявленное T.
  • Используется статическое поле, объявленное T, и поле не является константной переменной (§4.12.4).
  • T является классом верхнего уровня (§7.6), и выполняется утверждение assert (§14.10), лексически вложенное в T (§8.1.3).

(и несколько предложений слов ласки)

60 голосов
/ 05 января 2010

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

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

31 голосов
/ 05 января 2010

Ответы Кейта и Криса великолепны, я просто добавляю некоторые подробности для моего конкретного вопроса.

Статические блоки инициализации запускаются в порядке инициализации их классов. Итак, что это за порядок? Согласно JLS 12.4.1:

Класс T или интерфейсный тип T будут инициализированы непосредственно перед первым появлением любого из следующего:

  • T является классом и создается экземпляр T.
  • T является классом, и вызывается статический метод, объявленный T.
  • Назначено статическое поле, объявленное T.
  • Используется статическое поле, объявленное T, и поле не является постоянной переменной (§4.12.4).
  • T является классом верхнего уровня, и выполняется оператор assert (§14.10), лексически вложенный в T.

Вызов определенных отражающих методов в классе Class и в пакете java.lang.reflect также вызывает инициализацию класса или интерфейса. Класс или интерфейс не будут инициализированы ни при каких других обстоятельствах.

Чтобы проиллюстрировать, вот пример того, что происходит в примере:

  1. Введите основной
  2. Печать "СТАРТ"
  3. Попытка создать первый экземпляр Child, который требует инициализации Child
  4. Попытка инициализации Child приводит к инициализации Parent
  5. Попытка инициализации Parent вызывает инициализацию Grandparent
  6. В начале инициализации дедушки выполняется статический блок инициализации дедушки
  7. Технически, объект получает последнее слово в цепочке инициализации благодаря тому, что является родителем дедушки, но ему нечего внести
  8. После завершения блока статической инициализации дедушки программа возвращается к блоку статической инициализации Родителя
  9. После завершения блока статической инициализации Parent программа возвращается к блоку статической инициализации Child
  10. На этом этапе Child инициализируется, поэтому его конструктор может продолжить
  11. Поскольку на IAmAClassThatIsNeverUsed никогда не ссылаются, никогда не выполняется ни один из его кодов, включая блоки статического инициализатора
  12. Остальная часть этого пошагового руководства не касается статических инициализаторов и включена только для полноты
  13. Детский конструктор неявно вызывает super () (т. Е. Конструктор Parent)
  14. Родительский конструктор неявно вызывает super () (т.е. конструктор Дедушки)
  15. Конструктор бабушки и дедушки делает то же самое, что не имеет никакого эффекта (опять же, объект не имеет ничего, чтобы внести свой вклад)
  16. Сразу после вызова конструктора дедушки к super () приходит блок инициализатора экземпляра дедушки
  17. Остальная часть конструктора деда запускается, и конструктор завершается
  18. Программа возвращается к конструктору Parent, сразу после того, как ее вызов super () (т.е. конструктор Grandparent) разрешает
  19. Как и выше, инициализатор экземпляра Parent делает свое дело, а его конструктор завершает работу
  20. Аналогично, программа возвращается и завершает конструктор Child
  21. На данный момент объект был создан
  22. Печать "КОНЕЦ"
  23. Завершается нормально
1 голос
/ 01 мая 2014

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

Инициализация интерфейса состоит из выполнения инициализаторов для полей (констант), объявленных в интерфейсе.

Перед инициализацией класса его прямой суперкласс должен быть инициализирован, но интерфейсы, реализуемые классом, не инициализируются. Аналогично, суперинтерфейсы интерфейса не инициализируются до инициализации интерфейса.

0 голосов
/ 15 мая 2019
class A {
  public A() { 
    // 2
  }
}

class B extends A{
  static char x = 'x'; // 0
  char y = 'y'; // 3
  public B() { 
    // 4
  }

  public static void main(String[] args) {
    new B(); // 1
  }
}

Числа в комментарии указывают порядок оценки, чем меньше, тем раньше.

Как показал пример,

  1. статическая переменная
  2. основной
  3. конструктор суперкласса
  4. переменная экземпляра
  5. Конструктор
0 голосов
/ 17 ноября 2016

В одном случае статический блок не будет вызываться.

class Super {
    public static int i=10;
}
class Sub extends Super {
    static {
        system.out.println("Static block called");
    }
}
class Test {
    public static void main (String [] args) {
        system.out.println(Sub.i);
    } 
}

Вышеприведенный код выводит 10

0 голосов
/ 13 мая 2015

http://docs.oracle.com/javase/tutorial/java/javaOO/initial.html

Пожалуйста, проверьте документацию Java.

, затем четко упомянуто, независимо от того, как могут быть статические блоки, они будут выполняться как один блок в порядке их появления

Итак,

Насколько я понимаю, java рассматривает ваш код как

static{
i=1;
i=2;
}

static int i;

, поэтому вы получаете вывод 2

надеюсь, что это полезно

0 голосов
/ 25 февраля 2014

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

  • Статические инициализаторы вызываются в текстовом порядке, в котором они объявлены (от 12.4.2 )
  • Инициализаторы экземпляра вызываются в текстовом порядке, в котором они объявлены (от 12,5 )

Каждый выполняется так, как если бы это был один блок.

...