⏱️ 24 min read • Intermediate Level • Lesson 62
Completed on . You can revise this lesson or continue to the next topic.
Object cloning in Java creates a new object whose fields initially contain values copied from an existing object.
Java provides the protected Object.clone() method and the marker interface Cloneable. However, the default mechanism performs a shallow field copy, so mutable nested objects require special handling. Copy constructors and factory methods are often clearer alternatives.
Object cloning creates a separate object with field values copied from an existing object. The new object has a different identity, but whether nested objects are shared or copied depends on the copying strategy.
super.clone().Build a new object from an existing configuration.
Change a copy without intentionally changing the source object.
Capture state for editing, undo, or comparison workflows.
Create similar objects from a prepared prototype.
The clone() method is declared in java.lang.Object. It is protected and performs a field-by-field shallow copy when the object's class supports cloning.
protected native Object clone()
throws CloneNotSupportedException;
Object.clone() is protected, a class commonly overrides it with a public method and a more specific return type.
Cloneable is a marker interface. It declares no methods. Its presence tells Object.clone() that a field-by-field copy is permitted.
Cloneable does not automatically make clone() public.super.clone() without it causes CloneNotSupportedException.class Employee implements Cloneable {
private int id;
private String name;
Employee(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public Employee clone() {
try {
return (Employee) super.clone();
} catch (CloneNotSupportedException exception) {
throw new AssertionError(exception);
}
}
@Override
public String toString() {
return id + " - " + name;
}
public static void main(String[] args) {
Employee original = new Employee(101, "Ayan");
Employee copy = original.clone();
System.out.println(original);
System.out.println(copy);
System.out.println(original == copy);
}
}
Output:
101 - Ayan 101 - Ayan false
Cloneable.super.clone().A shallow copy duplicates primitive field values but copies object references as references. The original and clone therefore share the same mutable nested objects.
class Address {
String city;
Address(String city) {
this.city = city;
}
}
class Person implements Cloneable {
String name;
Address address;
Person(String name, Address address) {
this.name = name;
this.address = address;
}
@Override
public Person clone() {
try {
return (Person) super.clone();
} catch (CloneNotSupportedException exception) {
throw new AssertionError(exception);
}
}
public static void main(String[] args) {
Person original =
new Person("Ayan", new Address("Kanpur"));
Person copy = original.clone();
copy.address.city = "Lucknow";
System.out.println(original.address.city);
System.out.println(copy.address.city);
System.out.println(original.address == copy.address);
}
}
Output:
Lucknow Lucknow true
clone() method creates a shallow copy, meaning the address reference is copied as-is.Person objects share the exact same Address object in memory.Person instances refer to the same Address object.
A deep copy creates independent copies of mutable nested objects. Java does not perform this automatically; the class must define the desired copying behavior.
class Address implements Cloneable {
String city;
Address(String city) {
this.city = city;
}
@Override
public Address clone() {
try {
return (Address) super.clone();
} catch (CloneNotSupportedException exception) {
throw new AssertionError(exception);
}
}
}
class Person implements Cloneable {
String name;
Address address;
Person(String name, Address address) {
this.name = name;
this.address = address;
}
@Override
public Person clone() {
try {
Person copy = (Person) super.clone();
copy.address = address.clone();
return copy;
} catch (CloneNotSupportedException exception) {
throw new AssertionError(exception);
}
}
public static void main(String[] args) {
Person original =
new Person("Ayan", new Address("Kanpur"));
Person copy = original.clone();
copy.address.city = "Lucknow";
System.out.println(original.address.city);
System.out.println(copy.address.city);
System.out.println(original.address == copy.address);
}
}
Output:
Kanpur Lucknow false
clone() method in Person is overridden to first create a shallow copy and then explicitly clone the address field.Person objects now have independent Address objects.| Feature | Shallow Copy | Deep Copy |
|---|---|---|
| Primitive fields | Copied by value. | Copied by value. |
| Immutable references | Usually safe to share. | May be shared because their state cannot change. |
| Mutable nested objects | Shared. | Copied according to the design. |
| Default super.clone() | Provides this behavior. | Requires additional code. |
| Cost | Usually lower. | Usually higher. |
| Independence | Partial. | Greater, depending on copy depth. |
An overriding method may return a subtype of the parent method's return type. Therefore, an Employee clone method can return Employee instead of Object.
@Override
public Employee clone() {
try {
return (Employee) super.clone();
} catch (CloneNotSupportedException exception) {
throw new AssertionError(exception);
}
}
Calling super.clone() creates an object with the same runtime class as the original, even when the call originates in a parent implementation. Subclasses with additional mutable fields must still ensure those fields are copied correctly.
Every array type supports a public clone() method. Cloning a one-dimensional primitive array copies its values. Cloning an object array copies its element references, so referenced objects remain shared.
import java.util.Arrays;
class ArrayClone {
public static void main(String[] args) {
int[] original = {10, 20, 30};
int[] copy = original.clone();
copy[0] = 99;
System.out.println(Arrays.toString(original));
System.out.println(Arrays.toString(copy));
System.out.println(original == copy);
}
}
Output:
[10, 20, 30] [99, 20, 30] false
original == copy evaluates to false.A copy constructor accepts another object of the same class and explicitly copies its state. This approach avoids the protected clone API and makes shallow or deep-copy decisions visible.
class Address {
private final String city;
Address(String city) {
this.city = city;
}
Address(Address source) {
this.city = source.city;
}
}
class Customer {
private final String name;
private final Address address;
Customer(String name, Address address) {
this.name = name;
this.address = new Address(address);
}
Customer(Customer source) {
this.name = source.name;
this.address = new Address(source.address);
}
}
A static factory can provide a named copy operation and may return a different implementation when appropriate.
class Order {
private final int id;
private final String status;
private Order(int id, String status) {
this.id = id;
this.status = status;
}
static Order copyOf(Order source) {
return new Order(source.id, source.status);
}
}
Records are transparent data carriers. Creating a new record from an existing record's components is usually clearer than implementing Cloneable. Remember that mutable components are still references and may require defensive copies.
record Point(int x, int y) { }
class RecordCopy {
public static void main(String[] args) {
Point original = new Point(10, 20);
Point copy = new Point(original.x(), original.y());
System.out.println(original.equals(copy));
System.out.println(original == copy);
}
}
Output:
true false
Cloneable, records can be cleanly copied by passing their components into a new constructor.Point objects are distinct (different identities), so original == copy is false.equals() evaluates to true.
CloneNotSupportedException is a checked exception thrown by Object.clone() when the object's class does not implement Cloneable.
class InvalidClone {
@Override
public InvalidClone clone()
throws CloneNotSupportedException {
return (InvalidClone) super.clone();
}
public static void main(String[] args) {
try {
new InvalidClone().clone();
} catch (CloneNotSupportedException exception) {
System.out.println("Cloning is not supported");
}
}
}
Output:
Cloning is not supported
InvalidClone class calls super.clone() but does not implement the Cloneable marker interface.CloneNotSupportedException.Object.clone() is protected.Cloneable is a marker interface with no methods.super.clone() produces a shallow field copy.Object.clone().Cloneable but forgetting to expose clone().super.clone() performs a deep copy.super.clone() without implementing Cloneable.Object as the clone return type unnecessarily.Test your understanding with a quick quiz
Topic: Object-cloning | Language: Java
class Item implements Cloneable {
public Item clone() {
try { return (Item) super.clone(); }
catch (CloneNotSupportedException e) { throw new AssertionError(e); }
}
public static void main(String[] args) {
Item first = new Item();
Item second = first.clone();
System.out.println(first == second);
}
}class Box { int value; Box(int value) { this.value = value; } }
class Item implements Cloneable {
Box box = new Box(10);
public Item clone() {
try { return (Item) super.clone(); }
catch (CloneNotSupportedException e) { throw new AssertionError(e); }
}
public static void main(String[] args) {
Item first = new Item();
Item second = first.clone();
second.box.value = 99;
System.out.println(first.box.value);
}
}Great job! Continue learning Java step by step.
Mark this lesson as completed to track your learning progress. To keep track across devices, please login and save your learning progress.
Discussion
Ask questions, share suggestions, or discuss this lesson.