В каком порядке выполняются блоки инициализатора static/instance в Java?

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

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

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

старт
static - grandparent
static - parent
статический - ребенок!--8--> пример-дедушка
конструктор-дедушка
экземпляр-родитель
конструктор - родитель
экземпляр - ребенок
конструктор - ребенок
Конец

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

EDIT:

Я изменил свой пример кода, добавив Это в 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");
    }
}

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

7 ответов


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

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


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

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

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

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

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


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

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

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

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

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

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

  1. введите main
  2. печать "запустить"
  3. попытка создать первый экземпляр Child, который требует инициализации Child
  4. попытка инициализации дочернего элемента вызывает инициализацию Родитель!--4-->
  5. попытка инициализации родителя вызывает инициализацию бабушки и дедушки
  6. в начале инициализации дедушки и бабушки запускается статический блок инициализации дедушки и бабушки
  7. технически объект получает последнее слово в цепи инициализации в силу того, что он является родителем дедушки и бабушки, но ему нечего добавить
  8. после статического блока инициализации бабушки и дедушки заканчивается, программа возвращается к статическому блоку инициализации родителя
  9. после завершения статического блока инициализации родителя программа возвращается к статическому блоку инициализации ребенка
  10. В этот момент ребенок инициализируется, поэтому его конструктор может продолжить
  11. поскольку IAmAClassThatIsNeverUsed никогда не ссылается, ни один из его кода никогда не запускается, включая статические блоки инициализатора
  12. остальные это пошаговое руководство не касается статических инициализаторов и включено только для полноты
  13. конструктор ребенка неявно вызывает super () (т. е. конструктор родителя)
  14. конструктор родителя неявно вызывает super () (т. е. конструктор дедушки и бабушки)
  15. конструктор дедушки и бабушки делает то же самое, что не имеет никакого эффекта (опять же, объекту нечего вносить)
  16. сразу после вызова конструктора дедушки и бабушки в super () приходит Блок инициализатора экземпляра дедушки
  17. остальная часть конструктора конструктора дедушки и бабушки запускается, и конструктор завершается
  18. программа возвращается к родительскому конструктору сразу после вызова super () (т. е. конструктора дедушки и бабушки) разрешает
  19. как и выше, инициализатор экземпляра Parent делает свое дело, и его конструктор заканчивает
  20. аналогично, программа возвращается и завершает дочерние конструктор
  21. в этот момент объект был инстанцирован
  22. печать "END"
  23. завершить нормально

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

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

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


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

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

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


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

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

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

и

мое понимание здесь java смотрит ваш код как

static{
i=1;
i=2;
}

static int i;

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

Надежда это полезно


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

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