В Java инициализационные блоки (init-блоки) — это специальные блоки кода, которые выполняются при создании экземпляра объекта класса, но до вызова конструктора. Существуют два типа:
public class Example { static { System.out.println("Статический блок"); } { System.out.println("Блок экземпляра"); } public Example() { System.out.println("Конструктор"); } }
При создании нового объекта будет выведено:
Статический блок
Блок экземпляра
Конструктор
Использовать init-блоки есть смысл, когда требуется общая логика для всех конструкторов, либо сложная инициализация, не укладывающаяся в объявление переменной. Но чаще всего предпочтение отдается конструкторам.
Вопрос: В каком порядке инициализируются поля, static-блоки, instance-блоки и конструкторы при создании объекта?
Ответ:
История
В большом проекте возникла проблема с инициализацией: один из разработчиков переносил общую логику из конструктора в init-блок, не учтя порядок вызовов. В результате некоторые поля не были корректно проинициализированы до запуска логики, что вызывало NullPointerException на этапе создания объекта.
История
Повторное использование большого init-блока в абстрактном классе, от которого наследовались другие классы, привело к тому, что подклассы не переопределяли порядок инициализации должным образом. Это вызвало неожиданное поведение при наследовании и баги, связанные с порядком вызова init-блоков и конструкторов.
История
Разработчик предположил, что статические поля могут быть переинициализированы при каждом новом создании объекта, и добавил в static-блок логику очистки ресурсов. Это привело к тому, что ресурсы очищались только один раз при загрузке класса, а всё последующее управление памятью "отвалилось". Так как static-блок вызывается только единожды — это привело к утечкам и неправильному управлению ресурсами.