JavaRush /Курси /JAVA 25 SELF /Блоки ініціалізації

Блоки ініціалізації

JAVA 25 SELF
Рівень 15 , Лекція 4
Відкрита

1. Що таке блоки ініціалізації

У Java є два типи блоків ініціалізації:

  • Нестатичні (звичайні) блоки ініціалізації — виконуються під час кожного створення нового об’єкта, одразу після ініціалізації полів і до виклику конструктора.
  • Статичні блоки ініціалізації — виконуються один раз під час завантаження класу в пам’ять (до створення першого об’єкта або звернення до статичних полів/методів).

Нестатичний блок ініціалізації

Оголошується просто в тілі класу, без будь-яких слів на кшталт static:

public class User {
    private String name;

    // Нестатичний блок ініціалізації
    {
        System.out.println("Виконується нестатичний блок!");
        name = "Імʼя за замовчуванням";
    }

    public User() {
        System.out.println("Виконується конструктор!");
    }
}

Статичний блок ініціалізації

Оголошується з ключовим словом static:

public class Config {
    public static String appName;

    static {
        System.out.println("Виконується статичний блок!");
        appName = "Мій суперзастосунок";
    }
}

2. Порядок ініціалізації: хто головний?

У Java порядок ініціалізації елементів класу — це не просто «навмання», а суворо визначена послідовність. Якщо ви коли-небудь намагалися зібрати меблі з IKEA без інструкції, то розумієте, чому порядок важливий: переплутаєте кроки — й отримаєте не шафу, а артоб’єкт.

Порядок ініціалізації:

  1. Статичні поля і статичні блоки — у порядку їх оголошення в класі. Виконуються один раз під час завантаження класу.
  2. Нестатичні поля і нестатичні блоки — у порядку їх оголошення, під час кожного створення нового об’єкта.
  3. Конструктор — виконується після всіх нестатичних ініціалізацій.

Схематично

+-------------------------------+
|   Завантаження класу в JVM    |
+-------------------------------+
| 1. Статичні поля              |
| 2. Статичні блоки             |
|         ↓                     |
|   Створення об’єкта           |
|         ↓                     |
| 3. Нестатичні поля            |
| 4. Нестатичні блоки           |
| 5. Конструктор                |
+-------------------------------+

Приклад з виведенням

Напишімо клас, який покаже, у якому порядку все відбувається:

public class Demo {
    static String staticField = print("1. static поле");

    static {
        print("2. static блок");
    }

    String field = print("3. нестатичне поле");

    {
        print("4. нестатичний блок");
    }

    public Demo() {
        print("5. конструктор");
    }

    static String print(String msg) {
        System.out.println(msg);
        return msg;
    }

    public static void main(String[] args) {
        System.out.println("Створюємо перший об’єкт Demo:");
        Demo d1 = new Demo();

        System.out.println("\nСтворюємо другий об’єкт Demo:");
        Demo d2 = new Demo();
    }
}

Що буде на екрані?

1. static поле
2. static блок
Створюємо перший об’єкт Demo:
3. нестатичне поле
4. нестатичний блок
5. конструктор

Створюємо другий об’єкт Demo:
3. нестатичне поле
4. нестатичний блок
5. конструктор

Зверніть увагу: статичні частини (static) виконуються лише один раз — під час першого звернення до класу. Усе, що не static, — щоразу під час створення об’єкта.

3. Приклади коду: навіщо потрібні блоки ініціалізації

Коли конструктора не вистачає

Іноді частина ініціалізації має бути спільною для всіх конструкторів. Наприклад, у класі кілька конструкторів, і ви не хочете копіювати одну й ту саму ініціалізацію в кожен із них. У цьому разі зручно винести її в нестатичний блок:

public class Person {
    private String id;
    private String name;

    {
        // Цей код виконається перед будь-яким конструктором
        id = java.util.UUID.randomUUID().toString();
        System.out.println("Генеруємо унікальний id: " + id);
    }

    public Person() {
        System.out.println("Person() без параметрів");
    }

    public Person(String name) {
        this.name = name;
        System.out.println("Person(String name)");
    }
}

Результат: під час створення будь-якого об’єкта Person спершу буде згенеровано id, а потім виконається потрібний конструктор.

Ініціалізація складних статичних даних

Статичний блок часто використовують для ініціалізації «важких» або складних статичних полів, наприклад, для читання конфігурації з файлу, створення колекцій, під’єднання до бази даних тощо.

public class Settings {
    public static final java.util.Map<String, String> DEFAULTS;

    static {
        DEFAULTS = new java.util.HashMap<>();
        DEFAULTS.put("theme", "light");
        DEFAULTS.put("language", "uk");
        System.out.println("Статичний блок Settings: налаштування за замовчуванням");
    }
}

4. Корисні нюанси

Коли використовувати блоки ініціалізації

Коли варто використовувати

  • Для спільної ініціалізації, яка потрібна в усіх конструкторах.
  • Для складних статичних даних, які не можна виразити простим присвоєнням.
  • Для ініціалізації статичних ресурсів (наприклад, читання конфігураційного файлу під час запуску застосунку).

Коли НЕ варто використовувати

  • Якщо можна обійтися звичайним присвоєнням або конструктором — використовуйте їх.
  • Не варто «ховати» бізнес-логіку в блоках ініціалізації — це ускладнює читання та підтримку коду.
  • Якщо ініціалізація залежить від параметрів конструктора, використовуйте сам конструктор.

Не зловживайте блоками ініціалізації

Блоки ініціалізації — потужний, але не найуживаніший інструмент. У більшості випадків достатньо простого присвоєння або конструктора. Якщо в класі надто багато блоків ініціалізації, код стає нечітким і складним для підтримки.

Не використовуйте нестатичний блок для логіки, що залежить від параметрів конструктора

У нестатичному блоці не можна використовувати параметри конструктора, оскільки він виконується до конструктора. Якщо вам потрібно щось ініціалізувати на основі параметрів, робіть це в самому конструкторі.

Статичні блоки та наслідування

Статичні блоки ініціалізації не успадковуються. У кожного класу — свій статичний блок. Під час завантаження дочірнього класу спочатку виконається статичний блок базового класу, потім — статичний блок дочірнього.

5. Типові помилки під час роботи з блоками ініціалізації

Помилка № 1: Очікування, що нестатичний блок бачить параметри конструктора.
Багато новачків намагаються використовувати параметри конструктора в нестатичному блоці, але отримують помилку компіляції або неочікуваний результат. Пам’ятайте: нестатичний блок виконується до конструктора, а отже, жодних параметрів ще немає.

Помилка № 2: Забагато логіки в блоках ініціалізації.
Якщо в блоках ініціалізації з’являється складна логіка, код стає заплутаним. Краще виконувати основну роботу в конструкторі або окремих методах.

Помилка № 3: Кілька статичних блоків і порядок їх оголошення.
Якщо в класі кілька статичних блоків, вони виконуються в тому порядку, у якому оголошені в коді, разом зі статичними полями. Іноді це призводить до неочікуваних результатів, якщо, наприклад, один блок залежить від результату іншого.

Помилка № 4: Очікування, що статичний блок виконуватиметься під час кожного створення об’єкта.
static-блок виконується лише один раз — під час завантаження класу. Якщо ви розраховуєте на повторну ініціалізацію, цього не станеться.

Помилка № 5: Спроба звертатися до нестатичних полів зі статичного блоку.
У статичному блоці доступні лише статичні змінні та методи. Спроба звернутися до нестатичних (звичайних) полів викличе помилку компіляції.

1
Опитування
Інкапсуляція, рівень 15, лекція 4
Недоступний
Інкапсуляція
Принципи інкапсуляції
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ