Przyjrzyjmy się kolejności wykonywania kodu w blokach inicjalizacyjnych (statycznych i niestatycznych), konstruktorach oraz inicjalizacji pól statycznych i niestatycznych. W praktyce zrozumiemy, wykonując kod.

Na wejściu mamy klasę z pełnym zestawem wszystkich możliwych elementów:

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.");
    }
}

Teraz obok tej klasy utworzymy kolejną z główną metodą i uruchomimy ją:

public class Solution {
    public static void main(String args[]) {
        System.out.println("hello");
    }
}
W danych wyjściowych nie ma nic z klasy MyClass. Ponieważ nie było wywołań do MyClass, klasa w ogóle nie została załadowana. Spróbujmy teraz wywołać statyczną metodę print() klasy MyClass. Dwa razy.
public class Solution {
    public static void main(String args[]) {
        MyClass.print();
        MyClass.print();
    }
}

Wniosek:

Blok statyczny nr 1.
pole statyczne.
Blok statyczny nr 2.
metoda drukowania.
metoda drukowania.

Wykonano tylko statyczne bloki inicjalizacji i zainicjowano pole statyczne. I zdarzyło się to tylko raz. Faktem jest, że podczas drugiego wywołania metody print() klasa była już załadowana. Pamiętaj: pola statyczne i bloki inicjalizacyjne są wykonywane jednorazowo podczas pierwszej interakcji z klasą.

Należy zauważyć, że bloki statyczne są wykonywane, a pola inicjowane w kolejności, w jakiej zostały zadeklarowane.

Następnie, zamiast wywoływać metodę statyczną, spróbujmy stworzyć dwa obiekty naszej klasy:

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

Wniosek:

Blok statyczny nr 1.
pole statyczne.
Blok statyczny nr 2.
Blok inicjalizacyjny nr 1.
Nie pole statyczne.
Blok inicjalizacji nr 2.
Konstruktor.

Blok inicjalizacyjny nr 1.
Nie pole statyczne.
Blok inicjalizacji nr 2.
Konstruktor.

Najpierw statyczne bloki i pola przechodzą raz, a następnie za każdym razem, gdy tworzony jest obiekt, przetwarzane są niestatyczne bloki, pola i konstruktor. A jeśli pola i bloki inicjalizacyjne są przetwarzane w kolejności, w jakiej zostały zadeklarowane, to konstruktor jest przetwarzany na końcu, bez względu na to, gdzie jest zadeklarowany.

Skomplikujmy przykład i weźmy dwie klasy, z których jedna dziedziczy drugą:

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";
    }
}

Stwórzmy dwa obiekty klasy potomnej:

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

Wniosek:

Blok statyczny nr 1 klasy nadrzędnej.
Pole statyczne klasy nadrzędnej.
Blok statyczny nr 2 klasy nadrzędnej.
Blok statyczny nr 1 klasy potomnej.
Pole statyczne klasy potomnej.
Blok statyczny nr 2 klasy potomnej.
Blok inicjalizacji nr 1 klasy nadrzędnej.
Nie jest to pole statyczne klasy nadrzędnej.
Blok inicjalizacji nr 2 klasy nadrzędnej.
Konstruktor klasy nadrzędnej.
Pole niestatyczne klasy potomnej nr 1.
Blok inicjalizacji nr 1 klasy potomnej.
Pole niestatyczne klasy potomnej nr 2.
Blok inicjalizacji nr 2 klasy potomnej.
Konstruktor klasy potomnej.

Blok inicjalizacji nr 1 klasy nadrzędnej.
Nie jest to pole statyczne klasy nadrzędnej.
Blok inicjalizacji nr 2 klasy nadrzędnej.
Konstruktor klasy nadrzędnej.
Pole niestatyczne klasy potomnej nr 1.
Blok inicjalizacji nr 1 klasy potomnej.
Pole niestatyczne klasy potomnej nr 2.
Blok inicjalizacji nr 2 klasy potomnej.
Konstruktor klasy potomnej.

Z nowego widzimy, że statyczne bloki i zmienne klasy nadrzędnej działają przed statycznymi blokami i zmiennymi klasy potomnej. Tak samo jest z niestatycznymi blokami, zmiennymi i konstruktorami: najpierw klasa nadrzędna, potem klasa potomna. Dlaczego jest to potrzebne, możesz spojrzeć na przykład pola childNonStatic1 klasy potomnej. Aby go zainicjować, używana jest metoda klasy nadrzędnej, a ta metoda wykorzystuje odpowiednio zmienną klasy nadrzędnej, podczas inicjalizacji pola childNonStatic1 , klasa nadrzędna wraz z jej metodami musi być już załadowana, a zmienne klasy nadrzędnej muszą zostać zainicjowane.

W praktyce możesz nie natknąć się na klasy, które zawierają wszystkie wymienione elementy na raz, ale warto zapamiętać, co jest inicjowane dla czego. I to często pada w wywiadach.