ПрограммированиеJava разработчик

Что такое immutable-объекты в Java, в чём их ценность и как корректно реализовать собственный immutable класс?

Проходите собеседования с ИИ помощником Hintsage

Ответ.

Immutable-объекты — это объекты, состояние которых нельзя изменить после создания. Их основные свойства:

  • Все поля final;
  • Не содержат сеттеров;
  • К объектам внутри (например, коллекциям) нельзя получить изменяемую ссылку.

Преимущества immutable-объектов:

  • Безопасны для многопоточного доступа (thread-safe);
  • Легко кэшировать и переиспользовать как ключи в коллекциях;
  • Упрощают отладку и тестирование;
  • Меньше багов из-за неожиданных изменений состояния.

Пример реализации immutable-класса:

public final class Person { private final String name; private final int age; private final List<String> phones; public Person(String name, int age, List<String> phones) { this.name = name; this.age = age; // Защита от мутации переданного списка this.phones = Collections.unmodifiableList(new ArrayList<>(phones)); } public String getName() { return name; } public int getAge() { return age; } public List<String> getPhones() { return phones; } // Возвращаем read-only list }

Вопрос с подвохом.

Почему String в Java immutable и что бы случилось, если бы это было не так? Многие отвечают "для безопасности", но что это значит на практике?

Ответ:

String используется во многих местах: в качестве ключей в коллекциях, в логике безопасности (например, пароли). Если менять строку можно было бы через одну ссылку, это повлияло бы на все остальные ссылки на тот же объект, что делает невозможным правильную работу коллекций (например, HashMap — при вычислении hashCode) и может приводить к уязвимостям безопасности.

Примеры реальных ошибок из-за незнания тонкостей темы.


История

В большом банковском проекте передавали внутренние коллекции (список транзакций) обычным геттером. В результате список можно было изменить снаружи, нарушив инварианты (например, добавить транзакцию с некорректной датой). Это привело к потере данных, пока не стали возвращать Collections.unmodifiableList.

История

В корневом классе конфигурации хранились незащищённые поля-объекты (Date, List). В одном из потоков конфигурацию изменили, а во втором получили устаревшие или неконсистентные данные, из-за чего неправильно срабатывал бизнес-алгоритм.

История

В системе логина пароли хранились в изменяемом объекте. Через небезопасное обращение внезапно "утёк" пароль другого пользователя, т.к. тот же экземпляр объекта использовался многими потоками.