What is a defensive copy?
A Defensive Copy is creating a copy of data to protect the internal object from external modifications.
Basic Level
A Defensive Copy is creating a copy of data to protect the internal object from external modifications.
Like a photocopy of a document: you hand out a copy, and if someone writes on it, your original is unaffected.
Simple example
public class MyClass {
private Date date;
public MyClass(Date date) {
// BAD: this.date = date; // external variable can change
// GOOD:
this.date = new Date(date.getTime()); // created a copy
}
public Date getDate() {
return new Date(date.getTime()); // return a copy
}
}
Why this is needed
Without a copy, the calling code can change your internal data through the reference:
Date d = new Date();
MyClass obj = new MyClass(d);
d.setYear(2000); // Changed MyClass's internal object!
When NOT to make a defensive copy
- Immutable types (String, Integer, LocalDate) — copying is pointless
- Fully controlled internal API — caller guaranteed not to mutate
- Performance-critical code — copying on every call creates unacceptable overhead
Intermediate Level
Two points of protection
1. In the constructor (data input)
public class MyObject {
private final Date startDate;
private final List<String> items;
public MyObject(Date date, List<String> items) {
this.startDate = new Date(date.getTime());
this.items = new ArrayList<>(items);
}
}
2. In the getter (data output)
public Date getStartDate() {
return new Date(startDate.getTime());
}
public List<String> getItems() {
return Collections.unmodifiableList(new ArrayList<>(items));
}
Shallow vs Deep Copy
- Shallow Copy — the container is copied, but elements remain the same
List<String> copy = new ArrayList<>(original); - Deep Copy — all nested objects are recursively copied
List<User> copy = original.stream() .map(u -> new User(u.getName(), u.getAge())) .toList();
Modern approach (Java 10+)
this.items = List.copyOf(items); // immutable copy
Advanced Level
TOCTOU and operation ordering
Copy before validation:
public SecureClass(List<String> roles) {
List<String> copy = new ArrayList<>(roles); // 1. Copy
if (copy.contains("admin")) { // 2. Check
// ...
}
this.roles = List.copyOf(copy); // 3. Save
}
Otherwise a TOCTOU attack is possible: data changes between the check and the use.
Wrapper vs Copy
Collections.unmodifiableList(original) is a wrapper, not a copy. If original changes, the wrapper changes too. Use only for returning from a getter, but not in the constructor.
Performance
Defensive copying is an O(n) operation. In high-load systems:
- Avoid copying in hot loops
- Use persistent structures (Vavr) for structural sharing
- Caching copies is only safe if the data is truly static; otherwise you risk returning stale data.
Summary for Advanced
- Defensive copy — creating a “snapshot” of mutable data
- Mandatory condition for immutability when there are object fields
- Always make a copy before validation
Collections.unmodifiableList()— a wrapper, not a copy; useList.copyOf()for true protection
Interview Cheat Sheet
Must know:
- Defensive Copy — creating a copy of data to protect from external changes
- Two points of protection: constructor (input) and getter (output)
- Shallow Copy — container is copied, Deep Copy — all nested objects recursively
- TOCTOU: copy BEFORE validation, otherwise data changes between check and use
- Wrapper vs Copy:
unmodifiableList— wrapper (O(1)),List.copyOf— copy (O(n)) - When NOT to make: immutable types, internal API, performance-critical code
Frequent follow-up questions:
- What’s cheaper — wrapper or copy? — Wrapper O(1), copy O(n); but wrapper doesn’t protect from changes to the original
- When is shallow copy sufficient? — When the collection contains immutable objects (String, Integer)
- What is a TOCTOU attack? — Data changes between the check and the copy
List.copyOfvsnew ArrayList? — List.copyOf returns an immutable list; new ArrayList — mutable
Red flags (DO NOT say):
- “UnmodifiableList is a copy” — it’s a wrapper, changes to the original are visible
- “Defensive copy is always needed” — pointless for immutable types
- “clone() is a reliable way” — clone() is considered broken (Effective Java Item 13)
- “Copying after validation — correct” — you need to COPY BEFORE validation
Related topics:
- [[8. Is it enough to make all fields final for immutability]]
- [[9. What to do if a class field references a mutable object]]
- [[11. When should you make a defensive copy]]
- [[12. How to protect a collection from modification]]
- [[14. What is the difference between shallow copy and deep copy]]