ПрограммированиеMiddle/Senior Java разработчик

Что такое class initialization order в Java, как он работает и к чему может привести неправильное понимание порядка инициализации статических и нестатических членов?

Проходите собеседования с ИИ помощником Hintsage

Ответ.

Порядок инициализации класса в Java (class initialization order) определяет, в какой последовательности инициализируются поля, блоки и конструкторы. Кратко:

  1. Статические поля и статические инициализаторы выполняются в порядке их появления в классе, когда класс загружается JVM (один раз на класс).
  2. Нестатические поля и инициализаторы выполняются в порядке их появления КАЖДЫЙ раз при создании экземпляра, до вызова конструктора.
  3. Конструктор выполняется после инициализации всех полей и блоков.

Пример

class Parent { static { System.out.println("Parent static"); } { System.out.println("Parent init"); } Parent() { System.out.println("Parent constructor"); } } class Child extends Parent { static { System.out.println("Child static"); } { System.out.println("Child init"); } Child() { System.out.println("Child constructor"); } } // new Child() -> какой порядок?

Вывод будет:

  1. Parent static
  2. Child static
  3. Parent init
  4. Parent constructor
  5. Child init
  6. Child constructor

Сначала статик-блоки родителей, потом статик-блоки детей, потом нестатик-блоки и конструкторы в порядке наследования.

Вопрос с подвохом.

Вопрос: Когда именно будут выполнены статические инициализаторы в классе:

class Ex {
    static { System.out.println("static"); }
}

— при создании первого экземпляра или при первом применении любого статического метода/поля?

Ответ: Статический инициализатор выполняется при первом доступе к классу, включая доступ к любому статическому члену (методу/полю), а не только при создании экземпляра.

Примеры реальных ошибок из-за незнания тонкостей темы.


История

В банковском приложении статический пул соединений инициализировался в статик-блоке, но из-за обращения к статическому методу до создания первого экземпляра соединения еще не было создано. Это привело к NullPointerException при запуске под нагрузкой.


История

Служба логирования зависела от нестатического поля, которое инициализировалось после вызова конструктора родительского класса. Журналы записывались в null-логгер, и были утеряны важные сообщения об ошибках.


История

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