Java Object Cloning
Copy Java objects with clone(), the Cloneable interface, and the differences between shallow and deep copies.
Cloning an object means producing a new object with the same state — a copy you can mutate independently of the original. Java's built-in answer is Object.clone() plus the Cloneable marker interface, but the design has enough rough edges that most modern code reaches for a copy constructor or a factory method instead. This chapter shows both routes and the trap that lives between them.
The built-in route: Object.clone()
Object.clone() is protected and does a shallow field-by-field copy of the instance. To use it you have to:
- Make your class implement
Cloneable— a marker interface with no methods. Without it,clone()throwsCloneNotSupportedException. - Override
clone()to make itpublicand (usually) narrow the return type to the actual class.
public class Box implements Cloneable {
int size;
@Override
public Box clone() {
try {
return (Box) super.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError(e); // can't happen — we implement Cloneable
}
}
}super.clone() produces the actual copy. The try/catch is bureaucracy: the checked exception is declared on Object.clone, but our class implements Cloneable, so the exception is unreachable.
Shallow copy: what it actually does
A shallow copy duplicates the immediate fields. References inside the object are copied as references, not as new objects — so the original and the clone share whatever those references point to:
public class Person implements Cloneable {
String name;
int[] scores;
@Override
public Person clone() {
try { return (Person) super.clone(); }
catch (CloneNotSupportedException e) { throw new AssertionError(e); }
}
}
Person a = new Person();
a.scores = new int[]{1, 2, 3};
Person b = a.clone();
b.scores[0] = 99;
System.out.println(a.scores[0]); // 99 — they share the same arrayFor primitives and immutable values (String, Integer, LocalDate), shallow is fine. For mutable sub-objects, it's almost always wrong — mutating the clone reaches back into the original.
Deep copy: the fix
To get a true independent copy, override clone() to recursively copy mutable fields:
@Override
public Person clone() {
try {
Person copy = (Person) super.clone();
copy.scores = scores.clone(); // arrays have their own clone()
return copy;
} catch (CloneNotSupportedException e) {
throw new AssertionError(e);
}
}Arrays implement Cloneable natively and copy with .clone(). Collections don't — for a List<String> field you'd write copy.items = new ArrayList<>(items);. For a graph of your own mutable types, every type in the graph needs to participate.
Why Cloneable has a bad reputation
A few quirks make clone() awkward:
- It's a marker interface with no
clone()method —Cloneableitself doesn't expose anything, the contract lives onObject.clone(). - It bypasses constructors — the new object's fields are filled by the JVM, so any invariants you enforce in your constructor aren't re-checked.
- Subclasses inherit the obligation: if
Parentoverridesclone(), every subclass must keep the deep-copy logic in sync, or quietly inherit a broken shallow version. - The checked exception, the cast, the
super.clone()call — every override repeats the same noise.
The modern alternative: copy constructor
A copy constructor is just a constructor that takes an instance of the same class and copies its fields:
public class Person {
String name;
int[] scores;
public Person(Person other) {
this.name = other.name;
this.scores = other.scores.clone(); // deep where it matters
}
}
Person b = new Person(a);It runs through the normal constructor, so invariants are checked. It's plain Java — no marker interface, no CloneNotSupportedException, no cast. Subclasses just write their own copy constructor that calls super(other). Effective Java's recommendation is to prefer copy constructors (or static copyOf factories) over clone.
Collections-like classes already follow this pattern: new ArrayList<>(other), new HashMap<>(other), Set.copyOf(other).
Records and immutable types
Records are immutable, so they don't need cloning at all — share the same reference everywhere. If you need a modified copy, write small with... methods:
record Point(int x, int y) {
Point withX(int newX) { return new Point(newX, y); }
}This style — "construct a new instance with one field changed" — is usually clearer than cloning followed by mutation.
A worked example
What's next
Most of the trouble around cloning disappears if the class is immutable to begin with — nothing to copy defensively, no aliasing surprises, safe to share across threads. The next chapter walks through what it takes to design a class that way. Continue to Java immutable classes.
Practice
What does the default `Object.clone()` do to a field that holds a mutable list?