Skip to content
Java oops misc 6 min read

Object Cloning

Object cloning lets you create an exact duplicate of an existing Java object — a new instance with the same field values. It sounds simple, but the details (especially around nested objects) trip up even experienced developers.

What Is Object Cloning?

When you clone an object, Java creates a new object and copies the field values from the original. The cloned object lives at a different memory address, so modifying it does not affect the original — at least at the top level.

Java supports cloning through:

  1. The Cloneable marker interface (from java.lang)
  2. The clone() method defined on java.lang.Object

Note: Cloneable is a marker interface — it has no methods. It simply signals to the JVM that this class allows cloning. Without it, calling clone() throws CloneNotSupportedException.

Your First Clone

To make a class cloneable, implement Cloneable and override clone():

class Student implements Cloneable {
    String name;
    int age;

    Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone(); // JVM does the shallow copy
    }
}

public class Main {
    public static void main(String[] args) throws CloneNotSupportedException {
        Student s1 = new Student("Alice", 22);
        Student s2 = (Student) s1.clone();

        System.out.println(s1.name + " " + s1.age); // Alice 22
        System.out.println(s2.name + " " + s2.age); // Alice 22
        System.out.println(s1 == s2);               // false — different objects
    }
}

Output:

Alice 22
Alice 22
false

The two objects are independent. Changing s2.age won’t affect s1.age because int is a primitive — it’s copied by value.

Shallow Copy vs Deep Copy

This is the most important concept to understand about cloning.

Shallow Copy (the default)

super.clone() performs a shallow copy: it copies all field values directly. For primitives and String references, this is fine. But for mutable reference fields (like arrays or other objects), both the original and the clone end up pointing to the same nested object.

class Address {
    String city;
    Address(String city) { this.city = city; }
}

class Employee implements Cloneable {
    String name;
    Address address; // mutable reference field!

    Employee(String name, Address address) {
        this.name = name;
        this.address = address;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone(); // shallow — address reference is shared!
    }
}

public class ShallowDemo {
    public static void main(String[] args) throws CloneNotSupportedException {
        Employee e1 = new Employee("Bob", new Address("Delhi"));
        Employee e2 = (Employee) e1.clone();

        e2.address.city = "Mumbai"; // modifies BOTH e1 and e2!
        System.out.println(e1.address.city); // Mumbai  ← surprising!
        System.out.println(e2.address.city); // Mumbai
    }
}

Output:

Mumbai
Mumbai

Warning: A shallow clone shares nested object references with the original. Mutating a nested object through either reference affects both. This is one of the most common bugs introduced by naive use of clone().

Deep Copy

A deep copy creates new instances of every nested mutable object too. You do this manually inside clone():

class Address implements Cloneable {
    String city;
    Address(String city) { this.city = city; }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone(); // Address has only a String field — safe
    }
}

class Employee implements Cloneable {
    String name;
    Address address;

    Employee(String name, Address address) {
        this.name = name;
        this.address = address;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Employee cloned = (Employee) super.clone();
        cloned.address = (Address) address.clone(); // deep copy the nested object
        return cloned;
    }
}

public class DeepDemo {
    public static void main(String[] args) throws CloneNotSupportedException {
        Employee e1 = new Employee("Bob", new Address("Delhi"));
        Employee e2 = (Employee) e1.clone();

        e2.address.city = "Mumbai";
        System.out.println(e1.address.city); // Delhi  ← unchanged
        System.out.println(e2.address.city); // Mumbai
    }
}

Output:

Delhi
Mumbai

Now the two employees truly own independent Address objects.

Quick Comparison

FeatureShallow CopyDeep Copy
Primitive fieldsCopied correctlyCopied correctly
Immutable refs (String)Safe (immutable)Safe (immutable)
Mutable nested objectsShared referenceNew independent copy
Implementation effortMinimal (super.clone())Manual per nested field
Risk of mutation bugsHighLow

Cloning Arrays

Arrays also implement Cloneable. Calling array.clone() gives you a shallow copy of the array — a new array object, but the elements still point to the same objects (if they are references).

int[] original = {1, 2, 3};
int[] copy = original.clone();
copy[0] = 99;
System.out.println(original[0]); // 1 — primitives are safely copied
System.out.println(copy[0]);     // 99

For arrays of objects, you would need to clone each element individually to achieve a deep copy.

Alternatives to clone()

Because of the gotchas with Cloneable and clone(), many Java developers prefer cleaner alternatives:

Copy Constructor

A copy constructor takes an instance of the same class and manually copies fields:

class Point {
    int x, y;

    Point(int x, int y) { this.x = x; this.y = y; }

    // Copy constructor
    Point(Point other) {
        this.x = other.x;
        this.y = other.y;
    }
}

public class CopyCtorDemo {
    public static void main(String[] args) {
        Point p1 = new Point(3, 7);
        Point p2 = new Point(p1); // copy
        p2.x = 10;
        System.out.println(p1.x); // 3 — unaffected
    }
}

Tip: Copy constructors are explicit, type-safe, and do not require checked exception handling. Most Java style guides prefer them over clone() for new code.

Static Factory Method

class Config {
    String host;
    int port;

    private Config(String host, int port) { this.host = host; this.port = port; }

    public static Config of(String host, int port) { return new Config(host, port); }

    public Config copy() { return new Config(this.host, this.port); }
}

Serialization-Based Deep Copy

For complex object graphs, serialize to a byte array and deserialize back. This is slower but handles arbitrary depth automatically. The class (and all nested classes) must implement Serializable. See Serialization for details.

Under the Hood

When you call super.clone(), the JVM performs a bitwise copy of the object’s memory. It allocates a new block on the heap of exactly the same size as the original object and copies each byte verbatim. This is why:

  • Primitive fields are correctly duplicated (their value is literally the bytes).
  • Reference fields are also byte-copied — which means the new object holds the same memory address as the original’s reference, pointing to the same heap object. That’s what makes it “shallow.”

The Cloneable marker tells the JVM that this byte-copying is intentional and safe for this class. Without it, Object.clone() throws CloneNotSupportedException as a safety guard.

From a bytecode perspective, Object.clone() is a native method — it’s implemented in the JVM itself (in C/C++) for performance. There is no Java loop copying fields one by one; the JVM uses lower-level memory copy instructions, making shallow cloning very fast.

Note: Because clone() is protected in Object, you must override it as public if you want callers outside the same package to clone your objects.

When to Use (and Avoid) Cloning

Use clone() when:

  • You need a quick, lightweight copy of a simple value object.
  • You’re working with existing code or frameworks that already use Cloneable.

Avoid clone() when:

  • Your class has mutable reference fields — the shallow copy behavior is a trap.
  • You want a clear, readable API — copy constructors communicate intent better.
  • Your class is part of an inheritance hierarchy — subclasses must remember to call super.clone() correctly.

Tip: Effective Java (Joshua Bloch) famously calls the Cloneable/clone() design “deeply broken.” For new code, prefer copy constructors or static factory methods.

  • Object Classclone() is defined here; understand the full Object API
  • Constructors — copy constructors are the preferred cloning alternative
  • Serialization — use serialization for deep copy of complex object graphs
  • Arrays — arrays are Cloneable too; shallow vs deep applies there as well
  • Encapsulation — cloning interacts with access modifiers and field visibility
  • Classes & Objects — refresher on how objects live on the heap
Last updated June 13, 2026
Was this helpful?