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:
- The
Cloneablemarker interface (fromjava.lang) - The
clone()method defined onjava.lang.Object
Note:
Cloneableis a marker interface — it has no methods. It simply signals to the JVM that this class allows cloning. Without it, callingclone()throwsCloneNotSupportedException.
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
| Feature | Shallow Copy | Deep Copy |
|---|---|---|
| Primitive fields | Copied correctly | Copied correctly |
Immutable refs (String) | Safe (immutable) | Safe (immutable) |
| Mutable nested objects | Shared reference | New independent copy |
| Implementation effort | Minimal (super.clone()) | Manual per nested field |
| Risk of mutation bugs | High | Low |
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()isprotectedinObject, you must override it aspublicif 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.
Related Topics
- Object Class —
clone()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