skip to content

Cloning & clone()

Cloneable is a marker that switches on Object.clone, which does a shallow copy, bypasses constructors, and fights final fields. Interviewers ask this mostly to hear you explain why copy constructors or static copy factories are the recommended alternative.

part ofJavaoverview, primer and where to startread it →
on this pageshow

questions

5

What is the difference between a shallow copy and a deep copy, and how do you implement a deep clone in Java?

level: middleimportance: must knowfreq 68%

answer

  1. Shallow: copy box, share contents
  2. Deep: recursively copy mutable contents
  3. clone() is always shallow
  4. Reassign mutable ref fields after super.clone()
  5. final fields can't be reassigned → can't deep-clone them

basics

~20 s

A shallow copy duplicates the object but its reference fields still point to the same inner objects, so changes to those inner objects are seen by both. A deep copy also copies the inner objects, so the two are fully independent.

solid answer

~50 s

Object.clone() gives a shallow copy: primitives are copied by value, but a reference field in the clone points to the *same* object as in the original. If that referenced object is mutable, the original and the clone share it — mutating it through one is visible through the other. A deep copy recursively copies mutable referenced objects so the clone is fully independent. You implement it by overriding clone(): call super.clone() to get the shallow copy, then reassign each mutable reference field to a fresh copy (e.g. clone the array or list, or clone nested Cloneable objects). Note that final fields are a problem here — you cannot reassign them after super.clone(), which is one reason copy constructors are often preferred for deep copies. Immutable referenced objects (String, Integer) need no deep copy since sharing them is safe.

code

java · 14 lines
java
class Team implements Cloneable {
    List<String> members = new ArrayList<>();

    @Override
    public Team clone() {
        try {
            Team copy = (Team) super.clone();   // shallow: shares the list
            copy.members = new ArrayList<>(this.members); // deep: independent list
            return copy;
        } catch (CloneNotSupportedException e) {
            throw new AssertionError(e); // unreachable: we implement Cloneable
        }
    }
}

go deeper

for a junior

Can state that shallow shares inner objects and deep duplicates them, and recognize the shared-list bug when shown it.

for a middle

Implements a deep clone: super.clone() then copy each mutable reference field; knows immutable fields need no copy and arrays/collections need explicit copying.

for a senior

Reasons about how deep the copy must go, identifies the final-field conflict, and decides between clone and a copy constructor based on the field shape.

for a principal

Establishes copying conventions across a codebase (immutability-first to make copies trivial), and weighs deep-copy cost vs structural sharing / persistent data structures for large graphs.

## Shallow vs deep — the core idea An object's fields fall into two kinds: **primitive** values (`int`, `long`, `boolean`, …) held directly in the object, and **references** — pointers to *other* objects on the heap. A **shallow copy** copies the object's own slots. Primitive slots get the value. Reference slots get the *pointer*, so the copy and the original both point at the **same** referenced object. A **deep copy** copies the object *and* the objects it points to (recursively, as far down as needed), so the copy shares no mutable state with the original. `Object.clone()` always produces a **shallow** copy. ## Why shallow copies bite you ```java class Team implements Cloneable { List<String> members = new ArrayList<>(); @Override public Team clone() { try { return (Team) super.clone(); } catch (CloneNotSupportedException e) { throw new AssertionError(e); } } } Team a = new Team(); a.members.add("Ann"); Team b = a.clone(); // shallow b.members.add("Bob"); // mutates the SHARED list // a.members is now ["Ann", "Bob"] too — surprise aliasing ``` Because `members` is a reference, both `a` and `b` point to one `ArrayList`. This shared, unintended aliasing is the classic shallow-clone bug. ## Implementing a deep clone Start from `super.clone()` (so you still get a correct-typed, field-copied object), then replace each *mutable* reference field with a fresh copy: ```java @Override public Team clone() { try { Team copy = (Team) super.clone(); copy.members = new ArrayList<>(this.members); // copy the list return copy; } catch (CloneNotSupportedException e) { throw new AssertionError(e); } } ``` For nested objects that are themselves `Cloneable`, call their `clone()`. For arrays, `array.clone()` is a convenient shallow array copy (still shallow on element references, so deep arrays-of-objects need element-by-element copying). The copy must be deep *all the way down* to any mutable shared state, or the bug just moves one level deeper. ## What you do NOT need to deep-copy **Immutable** referenced objects — `String`, `Integer`, `LocalDate`, any properly immutable type — are safe to share because nobody can mutate them. Deep-copying them wastes memory and effort. ## The final-field obstacle Deep cloning works by *reassigning* fields after `super.clone()`. But a `final` field cannot be reassigned, so you cannot point it at a fresh deep copy. This is a fundamental conflict between cloning and immutability and a major reason `Effective Java` recommends a **copy constructor** instead, where you can pass deep-copied values straight to the constructor and keep fields `final`. ## Mental model Shallow = copy the box, share the contents. Deep = copy the box *and* recursively copy the contents. Choose deep only for the *mutable* contents.

  • Do you need to deep-copy a String field when deep-cloning an object?
    No. String is immutable, so sharing the same instance between the original and the clone is safe — no one can mutate it. Deep-copying immutable fields only wastes memory.
  • Why do final fields make deep cloning awkward?
    Deep cloning relies on reassigning reference fields to fresh copies after super.clone() returns. A final field cannot be reassigned after the object exists, so you cannot point it at a deep copy. A copy constructor avoids this by assigning the copied value at construction time.

saying these in an interview costs you the question

  • Claiming clone() does a deep copy by default
  • Deep-copying immutable types like String unnecessarily
  • Stopping deep copy one level too shallow (nested mutable state still shared)
  • Assuming array.clone() deep-copies an array of objects (it copies element references)

context

open as a page

Why does Effective Java discourage clone()/Cloneable, and what should you use instead?

level: seniorimportance: must knowfreq 62%

basics

~20 s

clone() has many flaws: Cloneable is a marker interface that magically changes Object.clone(), the default copy is shallow, it throws a checked exception, and it skips constructors so it clashes with final fields. Effective Java recommends a copy constructor or copy factory instead.

open as a page

How does Object.clone() work in Java, and what is the role of the Cloneable interface?

level: juniorimportance: should knowfreq 55%

basics

~20 s

clone() makes a copy of an object. To use it, your class must implement Cloneable; otherwise calling clone() throws CloneNotSupportedException. The default copy is shallow — it copies field values but not the objects they point to.

open as a page

Why is it significant that clone() bypasses constructors, and how does that affect final fields and invariants?

level: seniorimportance: should knowfreq 48%

basics

~20 s

clone() builds a new object by copying fields directly in the JVM, without running any constructor. So validation and setup in your constructor never run, and you cannot reassign final fields to fresh deep copies — both make clone() risky and hard to get right.

open as a page

How should the return type and access modifier of an overridden clone() be declared, and why?

level: middleimportance: nice to knowfreq 35%

basics

~20 s

Object.clone() is protected and returns Object. When you override it, make it public so callers can use it, and use a covariant return type (return your own class) so callers don't have to cast the result.

open as a page