What is the difference between a shallow and a deep copy when cloning in Java, and how do you implement a deep clone?
answer
- Shallow copies fields; deep copies what fields point to
- Primitives independent; references shared in shallow copy
- super.clone() then reassign mutable fields
- final fields can't be reassigned in clone() (a flaw)
- Cycles + nested objects make deep clone hard
basics
~20 sA shallow copy duplicates the object's fields but shares the objects those fields point to. A deep copy also duplicates the referenced objects so the copy is fully independent. Object.clone() gives a shallow copy; you must add deep copying yourself.
solid answer
~40 sObject.clone() performs a shallow copy: it copies each field's value, so primitive fields are independent but reference fields still point to the *same* underlying objects. If you mutate a shared list or array through one copy, the other sees the change — usually a bug. A deep copy clones the referenced objects too, recursively, so the two instances are independent. To deep-clone you override clone(), call super.clone() to get the shallow copy, then replace each mutable reference field with a copy of its contents (e.g. clone the internal array, deep-clone nested objects). Note that final fields can't be reassigned this way, which is one of clone()'s structural problems. Common alternatives are copy constructors or serialization-based deep copy.
code
java · 18 linesclass Cart implements Cloneable {
List<String> items = new ArrayList<>();
// SHALLOW (buggy): both carts share one list
// public Cart clone() { return (Cart) super.clone(); }
// DEEP: clone the mutable field too
@Override
public Cart clone() {
try {
Cart copy = (Cart) super.clone(); // shallow: items reference shared
copy.items = new ArrayList<>(this.items); // make it independent
return copy;
} catch (CloneNotSupportedException e) {
throw new AssertionError(e);
}
}
}go deeper
Can define shallow vs deep copy and state that clone() is shallow by default.
Implements a correct deep clone by reassigning mutable fields after super.clone(), and recognizes array/nested-object pitfalls.
Reasons about final-field conflicts, cyclic graphs, and inheritance cooperation; chooses copy constructors when those bite.
Sets team conventions (immutable value types to avoid copying, copy constructors over clone) and weighs serialization deep copy versus hand-written copy for large/cyclic graphs and performance.
## Two kinds of copy When you copy an object, the question is: do you copy only the object itself, or also everything it *refers to*? - **Shallow copy:** a new object is allocated and each field's *value* is copied. For a **primitive** field (`int`, `boolean`, etc.) the value *is* the data, so it's independent. For a **reference** field (a pointer to another object — a `List`, an array, a nested object), the *reference* is copied, meaning both the original and the copy point at the **same** referenced object. They share it. - **Deep copy:** the new object also gets fresh copies of every object it refers to, recursively. The two graphs of objects are completely independent — mutating one never affects the other. ## Why shallow copies cause bugs Consider an object holding a mutable `List`: ```java class Cart implements Cloneable { List<String> items = new ArrayList<>(); } ``` A shallow clone copies the `items` *reference*, so both carts share one list. Adding an item to the clone also appears in the original. This silent aliasing is the classic shallow-copy bug. ## Implementing a deep clone The recipe: take the shallow copy from `super.clone()`, then **reassign each mutable reference field to a fresh copy**: ```java @Override public Cart clone() { try { Cart copy = (Cart) super.clone(); copy.items = new ArrayList<>(this.items); // independent list return copy; } catch (CloneNotSupportedException e) { throw new AssertionError(e); } } ``` For nested mutable objects you must clone them too (and *their* mutable fields), recursing all the way down. Arrays have their own `clone()` that you should call (`arr.clone()`), but for an array of mutable objects that array-clone is itself shallow, so you then clone each element. ## The hard edges - **`final` fields:** you can't reassign a `final` field inside `clone()` (assignment after `super.clone()` is forbidden for `final`), so deep-cloning is incompatible with making mutable-reference fields `final`. This is a well-known flaw of the `clone()` approach. - **Cyclic graphs:** naive recursion on object graphs that contain cycles will loop forever; you'd need an identity map of already-copied objects. - **Inheritance:** every class in the hierarchy must cooperate, or `super.clone()` returns the wrong runtime type / misses fields. ## Alternatives that sidestep all this - **Copy constructor:** `new Cart(otherCart)` — you control the copy depth explicitly and can use `final` fields. - **Serialization deep copy:** serialize then deserialize the object graph; correct for arbitrary graphs but slow and requires `Serializable`. - **Libraries / records:** value-like types (records with immutable fields) often need no copying at all because they can be shared safely. ## Key terms - **Aliasing:** two references pointing at the same object, so a change via one is visible via the other. - **Object graph:** the network of objects reachable from a root via its reference fields. - **Recursive copy:** copying an object, then copying each object it references, then theirs, and so on.
- Why can't deep cloning with clone() work well with final reference fields?After super.clone() you must reassign each mutable reference field to a fresh copy, but a final field cannot be reassigned. So clone()-based deep copy forces those fields to be non-final, weakening immutability — a key reason copy constructors are preferred.
- How would serialization produce a deep copy, and what are its downsides?Serialize the object to a byte stream then deserialize it; the reconstructed graph is fully independent and handles cycles. Downsides: requires Serializable, is much slower, can break on transient/non-serializable fields, and has historical security concerns.
A shallow copy is like photocopying a folder's cover but sharing the same papers inside; a deep copy photocopies every page too, so the two folders are fully separate.
saying these in an interview costs you the question
- Believing Object.clone() recursively copies referenced objects
- Calling arr.clone() and assuming it deep-copies an array of mutable objects
- Making mutable reference fields final while relying on clone() to deep-copy them
- Ignoring cycles when hand-rolling a recursive deep copy