Давай разберемся в очередности выполнения кода в блоках инициализации (статических и не статических), конструкторах, а также инициализации статических и не статических полей. Разбираться будем на практике, выполняя код.

На входе у нас есть класс с полным набором всевозможных элементов:


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();
    }
}

Вывод:

Статический блок №1.
Статическое поле.
Статический блок №2.
Метод print.
Метод print.

Выполнились только статические блоки инициализации и инициализировалось статическое поле. Причем произошло это только один раз. Дело в том, что во время второго вызова метода print() класс уже был загружен. Запоминаем: статические поля и блоки инициализации выполняются один раз при первом взаимодействии с классом.

Обрати внимание, что выполнение статических блоков и инициализация полей идут в порядке их объявления.

Далее вместо вызова статического метода попробуем создать два объекта нашего класса:


public class Solution {
    public static void main(String args[]) {
        new MyClass();
        System.out.println();
        new MyClass();
    }
}

Вывод:

Статический блок №1.
Статическое поле.
Статический блок №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();
    }
}

Вывод:

Статический блок №1 родительского класса.
Статическое поле родительского класса.
Статический блок №2 родительского класса.
Статический блок №1 дочернего класса.
Статическое поле дочернего класса.
Статический блок №2 дочернего класса.
Блок инициализации №1 родительского класса.
Не статическое поле родительского класса.
Блок инициализации №2 родительского класса.
Конструктор родительского класса.
Не статическое поле дочернего класса №1.
Блок инициализации №1 дочернего класса.
Не статическое поле дочернего класса №2.
Блок инициализации №2 дочернего класса.
Конструктор дочернего класса.

Блок инициализации №1 родительского класса.
Не статическое поле родительского класса.
Блок инициализации №2 родительского класса.
Конструктор родительского класса.
Не статическое поле дочернего класса №1.
Блок инициализации №1 дочернего класса.
Не статическое поле дочернего класса №2.
Блок инициализации №2 дочернего класса.
Конструктор дочернего класса.

Из нового видим, что статические блоки и переменные родительского класса отрабатывают перед статическими блоками и переменными дочернего класса. То же самое и с нестатическими блоками и переменными и конструкторами: сначала родительский класс, затем дочерний. Для чего это нужно, можно посмотреть на примере поля childNonStatic1 дочернего класса. Для его инициализации используется метод родительского класса, а этот метод использует переменную родительского класса, соответственно во время иниицализации поля childNonStatic1, родительский класс с его методами уже должен быть загружен, и переменные родительского класса должны быть проинициализированы.

На практике ты можешь и не встретить классов, содержащих сразу все перечисленные элементы, но помнить, что за чем инициализируется будет полезно. А еще это часто спрашивают на собеседованиях 😊

undefined
13
Задача
Java Syntax Pro, 13 уровень, 3 лекция
Недоступна
Бухгалтерия
В классе Solution есть список сотрудников waitingEmployees, которые ждут зарплату, и список сотрудников alreadyGotSalaryEmployees, которые её уже получили. Необходимо реализовать метод paySalary(String), который в качестве аргумента принимает имя сотрудника, желающего получить зарплату. Метод должен
undefined
13
Задача
Java Syntax Pro, 13 уровень, 3 лекция
Недоступна
Переворачивание данных
Ты видишь рабочую программу, в которой массив int[] numbers заполняется числами в методе init(). Затем в методе reverse() происходит перестановка чисел в обратном порядке. Твоя задача — переписать код так, чтобы вместо массива int[] numbers использовался список ArrayList numbers. Название
undefined
13
Задача
Java Syntax Pro, 13 уровень, 3 лекция
Недоступна
Сортировка пузырьком
Перед тобой программа, которая сортирует элементы списка по возрастанию методом "пузырька". Перепиши код, чтобы вместо списка ArrayList numbers использовался массив int[] numbers. Методы main() и print() не принимают участие в проверке.