Питання 7 · Розділ 13

Що таке ключове слово final і як воно допомагає у створенні незмінних класів?

Ключове слово final в Java має три значення залежно від контексту:

Мовні версії: English Russian Ukrainian

Junior Level

Ключове слово final в Java має три значення залежно від контексту:

  • final для змінної = не можна переassign’ити
  • final для методу = не можна перевизначити в підкласі
  • final для класу = не можна успадковуватись

1. final змінна

final int MAX = 100;
// MAX = 200; // ПОМИЛКА! Не можна переassign'ити

2. final метод

public final void doSomething() {
    // Цей метод не можна перевизначити в підкласі
}

3. final клас

public final class MyImmutableClass {
    // Від цього класу не можна успадковуватись
}

Для незмінних класів

Використовуйте final для класу і для всіх полів:

public final class Point {
    private final int x;
    private final int y;
    // ...
}

Коли final на полі заважає

final несумісний з lazy initialization — якщо значення обчислюється при першому зверненні, поле не може бути final. Використовуйте holder pattern або AtomicReference.


Middle Level

final на рівні класу

Оголошення класу як final забороняє успадкування. Це критично важливо для незмінності: підклас не зможе додати змінні поля або перевизначити гетери.

final на рівні полів

Поле final можна ініціалізувати лише один раз — при оголошенні або в конструкторі:

public final class Config {
    private final String url;  // ініціалізується в конструкторі
    private final int timeout = 30; // або одразу при оголошенні

    public Config(String url) {
        this.url = url;
    }
}

final не захищає вміст

private final List<String> list = new ArrayList<>();
list.add("New Item"); // ДОЗВОЛЕНО! Посилання те саме, вміст змінився

Тому для колекцій потрібне додаткове захисне копіювання.

final методи

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


Senior Level

Safe Publication та JMM гарантії

final забезпечує “заморозку” (freeze) значення в кінці конструктора. Будь-який потік, що побачив посилання на об’єкт, гарантовано побачить його final поля в ініціалізованому стані. Без final можлива ситуація, коли потік бачить об’єкт, але його поля ще містять значення за замовчуванням (null або 0) через переупорядкування інструкцій процесором.

JIT-оптимізації

JIT-компілятор може агресивніше оптимізувати код:

  • Inlining — підстановка значень final полів напряму
  • Constant folding — обчислення виразів на етапі компіляції
  • Hoisting — винесення final полів з циклів, оскільки вони не змінюються

final та модульна система (Java 9+)

Починаючи з Java 16 (JEP 396 — Strong Encapsulation) доступ через reflection до final полів java.base заблоковано за замовчуванням. В Java 17 (JEP 403) посилено для всіх JDK internals. Потребує --add-opens.

Резюме для Senior

  • final клас запобігає поломці незмінності через успадкування
  • final поля забезпечують атомарну видимість даних у багатопотоковості (JMM freeze)
  • final посилання ≠ незмінність даних за посиланням — потрібна defensive copy
  • JIT використовує final для агресивних оптимізацій

🎯 Шпаргалка для інтерв’ю

Обов’язково знати:

  • final для змінної = не можна переassign’ити, для методу = не можна перевизначити, для класу = не можна успадковуватись
  • Для незмінності: final клас + final поля = безпечна публікація (JMM freeze)
  • final не захищає вміст за посиланням — final List можна змінювати, потрібна defensive copy
  • JIT оптимізації: inlining, constant folding, hoisting — все завдяки final
  • Safe publication: після конструктора усі final поля видимі любому потоку
  • Починаючи з Java 16, reflection до final полів java.base заблоковано (JEP 396)

Часті уточнюючі запитання:

  • Final поля можна ініціалізувати де? — При оголошенні або в конструкторі
  • final клас можна мокати в тестах? — Mockito потребує inline-mock-maker або bytebuddy-агент
  • final посилання = незмінні дані? — Ні, final List дозволяє add/remove — потрібна копія
  • Навіщо final метод? — Щоб підклас не перевизначив і не зламав незмінність

Червоні прапорці (НЕ говорити):

  • «Final полів достатньо для незмінності» — без final class та defensive copy недостатньо
  • «final = const» — final не можна переassign, але об’єкт за посиланням може змінюватися
  • «final метод робить клас незмінним» — лише конкретний метод захищений, не весь клас
  • «Reflection може змінити final поле» — в Java 16+ для java.base це неможливо

Пов’язані теми:

  • [[1. Що таке незмінний (ім’ютабельний) об’єкт]]
  • [[3. Як створити незмінний клас в Java]]
  • [[8. Чи достатньо зробити всі поля final для незмінності]]
  • [[16. Чому незмінний клас повинен бути final]]