skip to content

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

level: juniorimportance: should knowfreq 55%

answer

  1. Cloneable = empty marker; clone lives on Object
  2. No Cloneable → CloneNotSupportedException
  3. Always super.clone(), never new
  4. Default = shallow (refs shared)
  5. Widen to public + covariant return

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.

solid answer

~40 s

Object defines a protected native clone() that creates a new object and copies each field's value bit-for-bit. Cloneable is a marker interface (no methods): it signals that clone() is legal. If a class does NOT implement Cloneable and you call clone(), Object.clone() throws CloneNotSupportedException. To expose cloning you typically override clone(), make it public, call super.clone() to get the field-copied instance, and return it. Because clone() is declared on Object as protected and returns Object, you usually narrow the access to public and use a covariant return type so callers don't cast. The default copy is shallow: primitive fields are copied by value, but reference fields share the same referenced objects with the original.

go deeper

for a junior

Knows clone() copies an object, that Cloneable must be implemented, and that without it you get CloneNotSupportedException. Recognizes shallow vs deep at a high level.

for a middle

Can write a correct clone(): implement Cloneable, call super.clone(), catch the exception, widen to public. Explains why the default copy is shallow and what 'shared reference' means.

for a senior

Articulates why Cloneable is a flawed marker interface, the super.clone() subclass-correctness argument, covariant return types, and the boundary between shallow and deep copying. Can reason about when clone is acceptable vs a copy constructor.

for a principal

Frames cloning as an API design problem: discusses how the broken contract leaks into class hierarchies, sets team conventions (prefer copy constructors/factories), and weighs interop constraints (e.g. arrays/legacy APIs that still rely on clone).

## What 'cloning' means Cloning is producing a *new* object that is a copy of an existing one. In Java the JDK's built-in mechanism is the `clone()` method declared on `java.lang.Object`: ```java protected native Object clone() throws CloneNotSupportedException; ``` Three things about that signature matter: - **`protected`** — by default you can't call `obj.clone()` from arbitrary code; a class must override and *widen* it to `public` to let outsiders clone it. - **`native`** — the actual copying is done by the JVM, not Java code. It allocates a new object of the exact runtime class and copies every field's value. - **`throws CloneNotSupportedException`** — a *checked* exception. ## The Cloneable marker interface `java.lang.Cloneable` is a **marker interface**: an interface with **no methods**. Its only job is to act as a flag. The contract is unusual: `Object.clone()` checks at runtime whether `this` is an instance of `Cloneable`. If it is **not**, `clone()` throws `CloneNotSupportedException`. So `Cloneable` does not *provide* the `clone` method (that lives on `Object`); it merely *enables* the existing one. This is widely considered a design mistake — a marker interface that changes the behavior of a protected method on a different class. ## A minimal correct clone ```java public class Point implements Cloneable { int x, y; @Override public Point clone() { try { return (Point) super.clone(); // JVM field-copy } catch (CloneNotSupportedException e) { throw new AssertionError(e); // can't happen: we are Cloneable } } } ``` Key rules: 1. **Always call `super.clone()`** — that is what asks the JVM to do the real per-field copy and to produce an object of the correct runtime class. Building the copy with `new Point(...)` instead breaks subclasses (a subclass calling `super.clone()` would get a `Point`, not the subclass). 2. **Catch `CloneNotSupportedException`** — since you implement `Cloneable` it cannot actually be thrown, so most code rethrows it as an unchecked error. 3. **Narrow access to `public`** and use a **covariant return type** (`Point` instead of `Object`) so callers need no cast. ## Shallow copy The default copy is **shallow**: each field is copied by value. For primitive fields (`int`, `double`, `boolean`…) that copies the actual value. For reference fields it copies the *reference* — the copy and the original then point to the **same** referenced object. So mutating that shared object through one clone is visible through the other. Deep copying (recursively copying referenced mutable objects) is a separate, manual step. ## Why people avoid clone Because of the broken `Cloneable` contract, the shallow-by-default behavior, the checked exception, and the fact that `clone()` bypasses constructors, *Effective Java* recommends preferring a **copy constructor** or **copy factory** instead (covered in a follow-up).

  • Why must clone() call super.clone() rather than construct a new instance with new?
    super.clone() ultimately reaches Object.clone(), which the JVM uses to create an object of the actual runtime class and copy its fields. If a class used 'new ThisClass()' instead, a subclass that called super.clone() would receive an instance of the parent type, not the subclass — violating the clone contract that the returned object's class equals the original's.
  • What happens if you call clone() on a class that does not implement Cloneable?
    Object.clone() throws CloneNotSupportedException at runtime, because it checks 'this instanceof Cloneable' before copying.

saying these in an interview costs you the question

  • Thinking Cloneable declares the clone() method (it has no methods)
  • Implementing clone() by calling 'new' instead of super.clone() (breaks subclasses)
  • Believing the default clone makes a deep copy
  • Forgetting that clone() is protected on Object and must be widened to be callable

context