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

Які переваги дає використання незмінних об'єктів?

Незмінні об'єкти — це об'єкти, які не можна змінити після створення. Замість зміни вони створюють новий об'єкт з новими даними.

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

Junior Level

Незмінні об’єкти — це об’єкти, які не можна змінити після створення. Замість зміни вони створюють новий об’єкт з новими даними.

Основні переваги

  1. Простота розуміння — об’єкт завжди в одному стані, не потрібно відстежувати історію змін
  2. Безпека — ніхто не може випадково поміняти дані об’єкта
  3. Легше тестувати — об’єкт завжди поводиться однаково

Простий приклад

String s1 = "Hello";
String s2 = s1.concat(" World"); // s1 не змінився, s2 — новий рядок
// s1 як і раніше "Hello"

Ціна незмінності

  1. Excessive GC pressure при масовому створенні об’єктів (1000 конкатенацій = 1000 об’єктів)
  2. Overhead на копіювання великих структур (deep copy O(n))
  3. Незручний API — кожна зміна = створення нового об’єкта

Middle Level

Потокобезпечність без блокувань

Незмінні об’єкти потокобезпечні за замовчуванням:

  • Кілька потоків можуть одночасно читати об’єкт без ризику Race Condition
  • Не потрібні synchronized, ReentrantLock — немає блокувань, немає Deadlocks

Ідеальні ключі для HashMap

hashCode() об’єкта ніколи не зміниться — об’єкт не “загубиться” в бакеті хеш-таблиці.

Відсутність побічних ефектів

Методи повертають нові об’єкти замість зміни існуючих. Ви можете передати об’єкт у будь-який метод і бути впевненим, що його вміст не зміниться.

Повторне використання (патерн Flyweight)

  • String Pool — однакові рядки використовують один об’єкт
  • Integer Cache — числа від -128 до 127 повторно використовуються
  • Boolean.TRUE / Boolean.FALSE — єдині екземпляри

Приклад: безпечне повернення з гетера

public final class Config {
    private final Map<String, String> properties;

    public Config(Map<String, String> properties) {
        this.properties = Map.copyOf(properties);
    }

    public Map<String, String> getProperties() {
        return properties; // безпечно — Map.copyOf повернув незмінну мапу
    }
}

Senior Level

Гарантії Java Memory Model

Поля, позначені як final, отримують особливі гарантії видимості. Після завершення конструктора посилання на об’єкт вважається безпечно опублікованим — будь-який потік побачить коректні значення без додаткових бар’єрів пам’яті.

Вплив на Garbage Collection

  • Незмінні об’єкти створюються в Young Generation і там же помирають
  • Модифікація старого об’єкта в Old Generation потребує Write Barrier — незмінні об’єкти виключають такі накладні витрати
  • Посилання встановлюються один раз при створенні — GC не потрібно відстежувати міжпоколінні зміни

Масштабованість у розподілених системах

  • Lock-free доступ — читання без блокувань усуває contention між потоками, який при високій конкуренції може знизити throughput на 50-90%.
  • Side-effect free — відповідає принципам функціонального програмування
  • Cache Friendly — безпечне кешування без ризику “отруєння” даних

Резюме для Senior

  • Головний плюс: зниження когнітивного навантаження у багатопотоковому середовищі. Не потрібно думати: «А хто ще може змінити цей об’єкт?». Ви читаєте об’єкт — і впевнені, що він не зміниться під вами.
  • Виграш від відсутності блокувань перекриває витрати на алокацію при read-heavy навантаженні. Замість synchronized на кожному читанні — просто read без locks. На write-heavy навантаженні алокація нових об’єктів може стати вузьким місцем.
  • Незмінність — фундамент для надійних Domain Models
  • У високонавантажених системах незмінність перемагає за рахунок масштабованості

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

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

  • Потокобезпечність без блокувань — немає race condition, немає Deadlocks
  • Ідеальні ключі для HashMap — hashCode() ніколи не змінюється
  • Патерн Flyweight — String Pool, Integer Cache, повторне використання об’єктів
  • Side-effect free методи — відповідають принципам функціонального програмування
  • Safe publication через final поля — JMM гарантії видимості
  • Lock-free читання усуває contention (50-90% throughput при високій конкуренції)
  • GC виграє: немає Write Barriers, об’єкти в Young Generation очищуються безкоштовно
  • Cache Friendly — безпечне кешування без ризику “отруєння” даних

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

  • Чому незмінні об’єкти thread-safe? — Немає запису після створення = race condition неможливий
  • Як незмінність впливає на GC? — Нетривалі об’єкти очищуються в Minor GC, немає cross-generation посилань
  • Що таке Flyweight? — Патерн повторного використання однакових об’єктів (String Pool, Integer Cache)
  • Чи є ціна незмінності? — Excessive GC pressure при масовому створенні, overhead на копіювання

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

  • «Незмінність завжди сповільнює» — на read-heavy навантаженні без блокувань швидше
  • «synchronized потрібен для незмінних об’єктів» — вони потокобезпечні за замовчуванням
  • «Незмінні об’єкти не можна кешувати» — вони ІДЕАЛЬНІ для кешування
  • «Flyweight — це тільки для рядків» — Integer, Boolean теж використовують цей патерн

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

  • [[1. Що таке незмінний (ім’ютабельний) об’єкт]]
  • [[6. Чому незмінні об’єкти потокобезпечні]]
  • [[22. В чому переваги незмінних об’єктів для кешування]]
  • [[23. Як ім’ютабельність впливає на продуктивність]]