Когда нужно делать defensive copy (защитную копию)?
Защитную копию нужно делать, когда ваш класс получает от кого-то или отдаёт кому-то изменяемые данные.
Junior Level
Защитную копию нужно делать, когда ваш класс получает от кого-то или отдаёт кому-то изменяемые данные.
Когда делать копию
1. При получении данных (в конструкторе)
// Для полностью иммутабельного класса нет сеттеров.
// Этот пример показывает Builder pattern или конструктор с копированием:
public class MyClass {
private final List<String> items;
public MyClass(List<String> items) {
this.items = new ArrayList<>(items); // defensive copy в конструкторе
}
}
2. При возврате данных (в геттере)
public List<String> getItems() {
return new ArrayList<>(items); // копия исходящих данных
}
Когда НЕ нужно делать копию
- Примитивы (
int,boolean) — они не изменяемы String— иммутабелен- Внутренние данные, которые никогда не покидают класс
Middle Level
Основные сценарии
А. Массивы
public class MyClass {
private final int[] numbers;
public MyClass(int[] numbers) {
this.numbers = numbers.clone(); // или Arrays.copyOf(numbers)
}
public int[] getNumbers() {
return numbers.clone();
}
}
Б. Коллекции
this.list = List.copyOf(input); // Java 10+
this.list = new ArrayList<>(input); // Java 8
В. Date
this.date = new Date(date.getTime());
Когда копирование НЕ нужно
- Иммутабельные типы —
String,BigDecimal,LocalDate,Integer - Private данные — объект создан внутри класса и никогда не покидает его
- Transfer Objects — если класс по дизайну является мутабельным DTO
- Mutable DTO / Builder pattern — объекты по дизайну мутабельны
- Примитивы и обёртки (int, Integer, String) — иммутабельны по природе
Copy vs Unmodifiable
- В конструкторе — только Copy (
new ArrayList(original)) - В геттере — можно Copy или Unmodifiable (
Collections.unmodifiableList(original))
// List.copyOf выбрасывает NPE при null элементах, new ArrayList — допускает.
Senior Level
TOCTOU и порядок операций
Копируйте перед валидацией:
public SecureConfig(Map<String, String> config) {
Map<String, String> copy = new HashMap<>(config); // сначала копия
if (!copy.containsKey("auth")) throw new IllegalStateException(); // потом проверка
this.config = Map.copyOf(copy);
}
Без этого в многопоточной среде мапа может измениться после проверки, но до сохранения.
Производительность
List.copyOf()— оптимизирован в JDK 10+, может вернуть тот же объект если вход уже иммутабелен- Для больших коллекций в горячих циклах рассмотрите персистентные структуры данных (Vavr)
- Избегайте глубокого копирования огромных графов объектов
Резюме для Senior
- Делайте копии всего, что может быть изменено извне
- Сначала копируйте, потом валидируйте
- Используйте
List.copyOf()для современного защитного копирования - Не копируйте огромные массивы в “горячих” циклах без необходимости
- Различайте обёртку и копию: обёртка не защищает от изменений оригинала
🎯 Шпаргалка для интервью
Обязательно знать:
- Копировать при получении (конструктор) и при возврате (геттер) мутабельных данных
- НЕ нужно копировать: примитивы, String, LocalDate, Integer, private данные
- Массивы:
numbers.clone()илиArrays.copyOf() - Коллекции:
List.copyOf(input)(Java 10+) илиnew ArrayList<>(input)(Java 8) - Date:
new Date(date.getTime()) - Copy vs Unmodifiable: в конструкторе — только Copy, в геттере — Copy или Unmodifiable
- TOCTOU: сначала копируйте, потом валидируйте
Частые уточняющие вопросы:
- Когда копирование НЕ нужно? — Иммутабельные типы, private данные, Transfer Objects
List.copyOfvsnew ArrayList?* — List.copyOf возвращает иммутабельный; new ArrayList — мутабельный- Что если коллекция огромная? — Рассмотрите персистентные структуры (Vavr) или COW
- List.copyOf допускает null? — Нет, выбросит NPE; new ArrayList допускает
Красные флаги (НЕ говорить):
- «Defensive copy нужен для всех типов» — примитивы и String не нужно
- «Можно изменить мутабельный ключ в HashMap» — объект потеряется в бакете
- «UnmodifiableList в конструкторе — защита» — это обёртка, не копия
- «Копирование после валидации — безопасно» — TOCTOU-атака возможна
Связанные темы:
- [[9. Что делать, если поле класса ссылается на мутабельный объект]]
- [[10. Что такое defensive copy (защитная копия)]]
- [[12. Как защитить коллекцию от изменений]]
- [[13. Что такое Collections.unmodifiableList() и как это работает]]
- [[29. Как правильно работать с коллекциями в иммутабельных классах]]