Давай разберемся в очередности выполнения кода в блоках инициализации (статических и не статических), конструкторах, а также инициализации статических и не статических полей. Разбираться будем на практике, выполняя код.
На входе у нас есть класс с полным набором всевозможных элементов:
public class MyClass {
static {
System.out.println("Статический блок №1.");
}
public static String staticField = setStaticField();
public MyClass() {
System.out.println("Конструктор.");
}
static {
System.out.println("Статический блок №2.");
}
{
System.out.println("Блок инициализации №1.");
}
public String nonStaticField = setNonStaticField();
{
System.out.println("Блок инициализации №2.");
}
private String setNonStaticField() {
System.out.println("Не статическое поле.");
return "nonStaticField";
}
private static String setStaticField() {
System.out.println("Статическое поле.");
return "staticField";
}
public static void print() {
System.out.println("Метод print.");
}
}
Теперь рядом с этим классом создадим еще один, в нем метод main и запустим его:
public class Solution {
public static void main(String args[]) {
System.out.println("hello");
}
}
В выводе нет ничего из класса MyClass. Поскольку к MyClass не было обращений, класс вообще не был загружен.
Попробуем теперь вызвать у класса MyClass статический метод print(). Дважды.
public class Solution {
public static void main(String args[]) {
MyClass.print();
MyClass.print();
}
}
Вывод:
Статическое поле.
Статический блок №2.
Метод print.
Метод print.
Выполнились только статические блоки инициализации и инициализировалось статическое поле. Причем произошло это только один раз. Дело в том, что во время второго вызова метода print() класс уже был загружен. Запоминаем: статические поля и блоки инициализации выполняются один раз при первом взаимодействии с классом.
Обрати внимание, что выполнение статических блоков и инициализация полей идут в порядке их объявления.
Далее вместо вызова статического метода попробуем создать два объекта нашего класса:
public class Solution {
public static void main(String args[]) {
new MyClass();
System.out.println();
new MyClass();
}
}
Вывод:
Статическое поле.
Статический блок №2.
Блок инициализации №1.
Не статическое поле.
Блок инициализации №2.
Конструктор.
Блок инициализации №1.
Не статическое поле.
Блок инициализации №2.
Конструктор.
Сначала один раз идут статические блоки и поля, потом при каждом создании объекта отрабатывают нестатические блоки, поля, и конструктор. И если поля и блоки инициализации отрабатывают в порядке их объявления, то конструктор отрабатывает в конце, не зависимо от того в каком месте он объявлен.
Усложним пример, и возьмем два класса, причем один из них наследует другой:
public class ParentClass {
static {
System.out.println("Статический блок №1 родительского класса.");
}
public static String parentStatic = setParentStatic();
static {
System.out.println("Статический блок №2 родительского класса.");
}
{
System.out.println("Блок инициализации №1 родительского класса.");
}
public String parentNonStatic = setParentNonStatic();
{
System.out.println("Блок инициализации №2 родительского класса.");
}
public ParentClass() {
System.out.println("Конструктор родительского класса.");
}
private String setParentNonStatic() {
System.out.println("Не статическое поле родительского класса.");
return "parentNonStatic";
}
private static String setParentStatic() {
System.out.println("Статическое поле родительского класса.");
return "parentStatic";
}
public String setChildNonStatic1() {
System.out.println("Не статическое поле дочернего класса №1.");
return "childNonStatic2" + parentNonStatic;
}
}
public class ChildClass extends ParentClass {
static {
System.out.println("Статический блок №1 дочернего класса.");
}
public static String childStatic = setChildStatic();
static {
System.out.println("Статический блок №2 дочернего класса.");
}
public String childNonStatic1 = setChildNonStatic1();
{
System.out.println("Блок инициализации №1 дочернего класса.");
}
public String childNonStatic2 = setChildNonStatic2();
{
System.out.println("Блок инициализации №2 дочернего класса.");
}
public ChildClass() {
System.out.println("Конструктор дочернего класса.");
}
private String setChildNonStatic2() {
System.out.println("Не статическое поле дочернего класса №2.");
return "childNonStatic";
}
private static String setChildStatic() {
System.out.println("Статическое поле дочернего класса.");
return "childStatic";
}
}
Создадим два объекта дочернего класса:
public class Solution {
public static void main(String[] args) {
new ChildClass();
System.out.println();
new ChildClass();
}
}
Вывод:
Статическое поле родительского класса.
Статический блок №2 родительского класса.
Статический блок №1 дочернего класса.
Статическое поле дочернего класса.
Статический блок №2 дочернего класса.
Блок инициализации №1 родительского класса.
Не статическое поле родительского класса.
Блок инициализации №2 родительского класса.
Конструктор родительского класса.
Не статическое поле дочернего класса №1.
Блок инициализации №1 дочернего класса.
Не статическое поле дочернего класса №2.
Блок инициализации №2 дочернего класса.
Конструктор дочернего класса.
Блок инициализации №1 родительского класса.
Не статическое поле родительского класса.
Блок инициализации №2 родительского класса.
Конструктор родительского класса.
Не статическое поле дочернего класса №1.
Блок инициализации №1 дочернего класса.
Не статическое поле дочернего класса №2.
Блок инициализации №2 дочернего класса.
Конструктор дочернего класса.
Из нового видим, что статические блоки и переменные родительского класса отрабатывают перед статическими блоками и переменными дочернего класса. То же самое и с нестатическими блоками и переменными и конструкторами: сначала родительский класс, затем дочерний. Для чего это нужно, можно посмотреть на примере поля childNonStatic1 дочернего класса. Для его инициализации используется метод родительского класса, а этот метод использует переменную родительского класса, соответственно во время иниицализации поля childNonStatic1, родительский класс с его методами уже должен быть загружен, и переменные родительского класса должны быть проинициализированы.
На практике ты можешь и не встретить классов, содержащих сразу все перечисленные элементы, но помнить, что за чем инициализируется будет полезно. А еще это часто спрашивают на собеседованиях 😊
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ