В каком порядке выполняются блоки инициализатора статического/экземпляра в 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
статичный - бабушка и дедушка
static - parent
static - child
пример - grandparent
конструктор - grandparent
экземпляр - родительский конструктор - родительский
экземпляр - ребенок
конструктор - ребенок
END

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

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

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

Ответ 1

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

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

Ответ 2

См. разделы 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), и выполняется утверждение (§14.10), лексически вложенное в T (§8.1.3).

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

Ответ 3

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

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

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

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

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

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

  • Введите главное
  • Печать "СТАРТ"
  • Попытка создать первый экземпляр Child, для которого требуется инициализация Child
  • Попытка инициализации Child вызывает инициализацию родителя
  • Попытка инициализировать родительские причины инициализации бабушки и дедушки
  • В начале инициализации бабушки и дедушки запускается статический блок инициализации бабушки дедушки
  • Технически, объект получает последнее слово в цепочке инициализации благодаря тому, что он является родителем дедушки, но ему нечего вносить
  • После завершения статической инициализации блока grandparent программа возвращается в родительский статический блок инициализации
  • После завершения статической инициализации родительского пакета программа возвращается к блоку инициализации дочерних элементов
  • В этот момент Child инициализируется, поэтому его конструктор может продолжить
  • Поскольку IAmAClassThatIsNeverUsed никогда не ссылается, ни один из его кодов никогда не запускается, включая блоки статического инициализатора
  • Остальная часть этого пошагового руководства не касается статических инициализаторов и включена только для полноты
  • Конструктор Child неявно вызывает super() (т.е. конструктор родителя)
  • Родительский конструктор неявно вызывает super() (т.е. конструктор grandparent)
  • Конструктор grandparent делает то же самое, что не имеет никакого эффекта (опять же, Object ничего не может внести)
  • Сразу же после вызова конструктора grandparent на super() появляется блок инициализатора экземпляра grandparent.
  • Остальная часть конструктора конструктора grandparent запускается, и конструктор завершает
  • Программа возвращается к родительскому конструктору сразу после вызова супер() (т.е. конструктора дедушки и бабушки) разрешает
  • Как и выше, инициализатор родительского экземпляра делает свою вещь, а его конструктор заканчивается
  • Аналогично, программа возвращает и завершает конструктор Child
  • В этот момент объект был создан
  • Печать "END"
  • Обычно завершается

Ответ 4

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

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

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

Ответ 5

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

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

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

Ответ 6

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

проверьте документацию java.

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

Итак,

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

static{
i=1;
i=2;
}

static int i;

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

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

Ответ 7

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

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

Ответ 8

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. конструктор