Гарантирует ли модификатор static final неизменяемость объекта в Java?

«Гарантирует ли модификатор static final неизменяемость объекта в Java?» — вопрос из категории Java Core, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Нет. Модификатор static final гарантирует только неизменяемость ссылки на объект, но не защищает его внутреннее состояние (поля).

Для создания полностью неизменяемого (immutable) объекта необходимо:

  1. Объявить класс как final, чтобы запретить наследование и переопределение методов.
  2. Объявить все поля как private final.
  3. Не предоставлять сеттеров (setter methods).
  4. Инициализировать все поля через конструктор.
  5. Защитить изменяемые (mutable) поля:
    • Не хранить и не возвращать прямые ссылки на внешние изменяемые объекты (например, массивы или коллекции).
    • Использовать защитное копирование (defensive copying) при сохранении и возврате таких полей.

Пример неизменяемого класса с коллекцией:

import java.util.*;

public final class ImmutablePerson {
    private final String name;
    private final List<String> hobbies;

    public ImmutablePerson(String name, List<String> hobbies) {
        this.name = name;
        // Защитное копирование: создаем новый список на основе переданного
        this.hobbies = new ArrayList<>(hobbies);
    }

    public List<String> getHobbies() {
        // Возвращаем неизменяемую обертку, а не оригинальный список
        return Collections.unmodifiableList(new ArrayList<>(hobbies));
    }
}

Почему это важно: Без защитного копирования вызывающий код может изменить исходную коллекцию hobbies после создания объекта или модифицировать список, полученный через геттер, что нарушит инвариант неизменяемости.