Давай розберемося в черговості виконання коду в блоках ініціалізації (статичних та нестатичних), конструкторах, а також ініціалізації статичних і нестатичних полів. Розбиратимемося на практиці, виконуючи код.
На вході у нас є клас із повним набором різноманітних елементів:
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 батьківський клас із його методами вже повинен бути завантажений, і змінні батьківського класу повинні бути проініціалізовані.
На практиці ти можеш і не зустріти класів, що містять відразу всі перелічені елементи, але пам’ятати, що за чим ініціалізується буде корисним. А ще про це часто запитують на співбесідах 😊