Блоки и порядок инициализации

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

class Parent {
    static int ps = log("parent static");
    int pi = log("parent field");
    { log("parent instance block"); }
    Parent() { log("parent constructor"); }
 
    static int log(String value) {
        System.out.println(value);
        return 0;
    }
}
 
class Child extends Parent {
    static int cs = log("child static");
    int ci = log("child field");
    { log("child instance block"); }
    Child() { log("child constructor"); }
}

При первом активном использовании new Child() порядок такой:

  1. инициализация класса Parent: его статические поля и блоки в текстовом порядке;
  2. инициализация класса Child: его статические поля и блоки;
  3. память объекта заполнена значениями по умолчанию;
  4. экземплярные поля и блоки Parent в текстовом порядке;
  5. тело конструктора Parent;
  6. экземплярные поля и блоки Child;
  7. тело конструктора Child.

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

Инициализация класса

Загрузка, linking и инициализация — разные этапы. Инициализацию обычно запускают new, вызов статического метода, чтение/запись неконстантного статического поля или reflection. JVM синхронизирует инициализацию класса.

Если static-инициализатор завершился с ошибкой, первый вызов получает ExceptionInInitializerError; следующие попытки обычно видят NoClassDefFoundError для ошибочного класса.

Ошибки

  • опираться на значения полей, объявленных ниже, через косвенный вызов;
  • вызывать переопределяемый метод из конструктора;
  • выполнять сеть или тяжёлую работу в static-блоке;
  • путать значение поля по умолчанию с явной инициализацией.

На собеседовании

Что раньше: поля или instance-блок? В том текстовом порядке, в котором они объявлены.

Когда выполняется static-блок? Один раз при инициализации класса данным загрузчиком.

Сначала родитель или наследник? На обоих уровнях сначала родитель.

Что получат поля до инициализаторов? Нулевые значения JVM: 0, false, null.

Связано: Исключения в блоке инициализации, Class loader, super java.