library
intermediateCloning: Shallow vs Deep Copy
Understand Object.clone(), its problems, and the preferred alternatives (copy constructors, factory methods).
Copying an object sounds simple until the object contains other objects. A shallow copy copies the outer object only, so both copies share whatever is inside. A deep copy copies the inner objects too, so the two are completely separate.
Shallow copy = photocopying a document with sticky notes. The photocopy looks the same, but the sticky notes (references) still point to the originals. If someone moves a sticky note, both documents are affected.
Key Concepts
1
Get this wrong and changing one copy silently changes the other.
2
Cloning is where object identity and mutability meet, and the Java mechanism for it is
widely considered a design mistake — which makes "how would you copy this object?" a question about
judgement rather than API recall.
3
Shallow versus deep is about what the copy shares. A shallow copy duplicates the fields; for reference
fields that means both objects now point at the same nested object. Mutating that nested object through
either reference is visible through both. A deep copy duplicates the nested objects too, recursively, so
the two graphs are independent.
4
Object.clone() is shallow, and the mechanism around it is awkward. Cloneable is a marker interface
with no methods; it does not give you clone(), it merely stops Object.clone() from throwing
CloneNotSupportedException. clone() itself is protected, so you must override it to make it usable,
and it bypasses constructors entirely — the object is created field by field, so any invariant your
constructor enforces is not applied. final fields cannot be reassigned in clone(), which makes deep
copying immutable-ish classes awkward.
Object.clone()Cloneableclone()CloneNotSupportedExceptionprotected
5
A copy constructor is the usual better answer. new Address(other) is explicit, works with final
fields, runs your constructor's validation, and has no checked exception. A static factory — Address.copyOf
— reads even better and can return a cached instance for immutable values. Josh Bloch's advice to prefer
copy constructors over Cloneable is standard, and citing the reasoning rather than the conclusion is what
lands in an interview.
new Address(other)finalAddress.copyOfCloneable
6
Serialization-based deep copy is the lazy option with real costs. Round-tripping through
ObjectOutputStream, or a JSON library, produces a deep copy without writing per-class code — and is
slow, requires everything to be serialisable, silently drops transient fields, and can be a security
problem with untrusted input. It is defensible for a deep tree in a test fixture and hard to defend on a
hot path.
ObjectOutputStreamtransient
7
The best copy is often no copy. If the object is immutable, sharing is safe and copying is waste.
Making value types immutable — final fields, no setters, defensive copies of any mutable input, record
where it fits — removes the whole question, which is the answer a senior candidate reaches for.
record
8
What the interviewer is probing.1. "Shallow or deep?" Probing: whether you can state the consequence. Stalls: definitions only.
Moves up: what happens when the nested object is mutated through one reference — and that the bug appears
far from the copy.
9
2. "How does Object.clone() behave?" Probing: mechanism. Stalls: "It copies the object."
Moves up: field-by-field shallow copy, bypasses constructors, needs Cloneable to avoid an exception,
protected so it must be overridden.
Object.clone()Cloneableprotected
10
3. "How would you actually copy this class?" Probing: judgement. Stalls: "Implement Cloneable."
Moves up: a copy constructor or static factory, with the reasons — validation runs, final fields work,
no checked exception.
final
11
4. "Deep copy a nested graph?" Probing: awareness of cycles. Stalls: "Recurse." Moves up:
recursion plus an identity map to handle shared references and cycles, or serialization with its costs
named.
12
5. "When do you not need to copy?" Probing: the design answer. Stalls: "Always copy to be safe."
Moves up: immutable objects can be shared freely; making the type immutable removes the question
entirely.
13
What each approach gives you14
clone() copy ctor serialize record/
immutable
------------------------------------------------------------------
Depth shallow you choose deep n/a
Runs constructor NO yes no yes
Works with final no yes yes yes
fields
Checked exception yes no yes no
Needs Cloneable yes no Serializable no
Handles cycles n/a you handle yes n/a
Speed fastest fast slowest free
Drops transient no no YES n/a15
Shallow copy, visible failure16
Order a = new Order(customer, items); // items is a List
Order b = a.shallowCopy();
b.items().add(newItem);
a.items().size(); // changed too - same list instance17
Deep copy with shared references18
Use an IdentityHashMap<Object,Object> during the recursion so a
node referenced twice is copied once, and a cycle terminates:19
copy(node, seen):
if seen.containsKey(node): return seen.get(node)
fresh = shallowCopy(node)
seen.put(node, fresh)
for each reference field: fresh.f = copy(node.f, seen)
return fresh