Инициализаторы экземпляров Java — это блоки кода, содержащие инструкции для запуска при каждом создании нового экземпляра класса. Мы можем использовать блоки инициализаторов для записи инициализационного входа, общего для всех конструкторов, который в противном случае должен быть написан в каждом конструкторе отдельно.
1. Синтаксис
Блок инициализатора экземпляра создается с помощью фигурных скобок. Операторы инициализации объекта записываются внутри фигурных скобок.
public class DemoClass {public DemoClass() {//statements}/* initializer block */{//statements}}
2. Как работает блок инициализатора экземпляра?
Во время компиляции компилятор Java копирует байт-код блока инициализатора сразу после оператора super() в каждом конструкторе.
public class Main {public Main() {System.out.println("Statement in constructor");}{System.out.println("Statement in initializer");}}
Рассмотрим приведенную выше программу, где блоки конструктора и инициализатора имеют один оператор печати. После компиляции кода байт-код логически структурирован следующим образом:
public class Main {public Main() {super();System.out.println("Statement in initializer");System.out.println("Statement in constructor");}}
мы можем проверить эту структуру, выполнив программу, которая выведет следующий вывод:
Statement in initializerStatement in constructor
3. Блоки инициализатора выполняются последовательно
Если в классе есть несколько блоков инициализатора, они выполняются в той же последовательности, в которой они указаны в определении класса. В следующем примере блок инициализатора 1 всегда выполняется перед блоком 2.
public class Main {public Main() {System.out.println("Statement in constructor 2");}{System.out.println("Statement in initializer 1");}{System.out.println("Statement in initializer 2");}}
Программа выводит данные при создании экземпляра с использованием конструктора по умолчанию:
Statement in initializer 1Statement in initializer 2Statement in constructor 2
4. Наследование
Если и родительский, и дочерний классы имеют блоки инициализатора, то последовательность действий при создании экземпляра дочернего класса будет следующей:
- Вызывается конструктор дочернего класса.
- Конструктор дочернего класса имеет первый оператор super()(или указанный явный конструктор), поэтому вызывается конструктор родительского класса.
- Инициализаторы родителя выполняются в порядке их появления.
- Выполняются операторы конструктора родителя.
- Инициализаторы дочерних классов выполняются в порядке их появления.
- Выполняются операторы конструктора дочернего класса.
Давайте рассмотрим вышесказанное в действии:
class ParentClass {public ParentClass() {System.out.println("In ParentClass Constructor");}{System.out.println("In ParentClass Instance Initializer");}}class ChildClass extends ParentClass {public ChildClass() {super(); //If not provided, JVM will insert itSystem.out.println("In ChildClass Constructor");}{System.out.println("In ChildClass Instance Initializer 1");}{System.out.println("In ChildClass Instance Initializer 2");}}
Когда мы создаем экземпляр ChildClass, программа выводит:
In ParentClass Instance InitializerIn ParentClass ConstructorIn ChildClass Instance Initializer 1In ChildClass Instance Initializer 2In ChildClass Constructor
5. Особенности инициализаторов экземпляров
Инициализаторы экземпляров имеют следующие особенности.
- Мы можем определить несколько инициализаторов в классе.
- Все инициализаторы выполняются последовательно в том порядке, в котором они указаны в теле класса.
- Инициализаторы запускаются после вызова конструктора родительского класса и перед выполнением конструктора дочернего класса. Обратите внимание, что Java вставляет конструктор по умолчанию родительского класса super(), если мы явно не предоставляем конструктор в качестве первого оператора в конструкторе дочернего класса.
- После выполнения всех инициализаторов выполняются операторы конструктора.
- Мы можем использовать вызов конструкторов этого класса и родительского класса внутри инициализаторов.