В каком порядке выполняются блоки инициализатора 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");
    }
}

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

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

EDIT:

Я изменил свой пример кода, добавив Это для загрузки.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 84

7 ответов:

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

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

см. раздел 12.4 и 12.5 в JLS version 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).

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

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

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

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

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

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

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

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