Неизменяемые объекты не меняют своё наблюдаемое состояние после создания. Примеры: String, Integer, Long, Double, Boolean, LocalDate, Instant и Duration. Для собственного класса нужны защита полей и отсутствие путей изменения вложенного состояния. final запрещает переназначение ссылки, но не изменение объекта. Records неизменяемы поверхностно: изменяемые компоненты требуют отдельной защиты.
Какие неизменяемые объекты есть в Java и как создать собственный?
Примеры immutable-классов Java, ограничения final и records, защитное копирование и простой неизменяемый Point.
Короткий ответ
Что ответить на собеседовании
Подробный разбор
Ответ с пояснениями
Immutable-объект не позволяет изменить своё наблюдаемое состояние после создания. В Java привычные примеры — String, классы-обёртки Integer, Long, Double, Boolean, а также LocalDate, LocalDateTime, Instant и Duration из java.time. Вызов plusDays возвращает значение даты, не изменяя исходный объект. StringBuilder и java.util.Date, напротив, изменяемы. String, java.time.
Неизменяемость объекта не равна неизменяемости переменной. Ссылку на String можно переназначить, не меняя саму строку. И наоборот, final List<String> запрещает присвоить переменной другой список, но не запрещает add или clear у изменяемого списка. Такую семантику final задаёт JLS.
Простой собственный класс точки, не java.awt.Point:
public final class Point {
private final int x;
private final int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public int x() { return x; }
public int y() { return y; }
public Point withX(int value) {
return new Point(value, y);
}
}
Здесь поля примитивные, сеттеров нет, наследник не может добавить изменяющее поведение. withX создаёт другую точку; исходная остаётся прежней.
Если поля содержат массивы, коллекции или другие изменяемые объекты, одного private final недостаточно. Нельзя сохранять неконтролируемую внешнюю ссылку или возвращать внутреннее изменяемое состояние. Применяют защитные копии на входе и выходе либо действительно неизменяемые компоненты; копия контейнера не делает его элементы неизменяемыми. Не следует также публиковать this до завершения конструктора. Рекомендации Oracle.
record автоматически создаёт final-поля компонентов, но обеспечивает лишь поверхностную неизменяемость. Record со списком может оставаться изменяемым через этот список; защитное копирование требует явного решения. Record с двумя int подходит для неизменяемой точки. Документация Record.