Deep Copy vs Shallow Copy in Java
In Java, copying objects can be done in two ways — shallow copy and deep copy.
Understanding the difference between them is crucial when working with mutable objects, arrays, or custom classes.
Both techniques create new objects, but they differ in how they handle object references inside objects.
What is a Shallow Copy?
A shallow copy creates a new object, but copies references of nested objects, not their actual values. That means both the original and the copied object share the same referenced objects in memory.
If you modify the inner object in the copy, it will also affect the original.
Example – Shallow Copy
class Address {
String city;
Address(String city) {
this.city = city;
}
}
class Student implements Cloneable {
int id;
String name;
Address address;
Student(int id, String name, Address address) {
this.id = id;
this.name = name;
this.address = address;
}
public Object clone() throws CloneNotSupportedException {
return super.clone(); // shallow copy
}
}
public class ShallowCopyExample {
public static void main(String[] args) throws CloneNotSupportedException {
Address addr = new Address("Delhi");
Student s1 = new Student(101, "Aman", addr);
Student s2 = (Student) s1.clone();
s2.address.city = "Mumbai"; // change city in cloned object
System.out.println(s1.address.city); // also changes to Mumbai
}
}
Output:
Mumbai
Explanation:
Both s1 and s2 share the same Address reference. So, changing the address in s2 affects s1 too.
What is a Deep Copy?
A deep copy creates a new object along with new copies of all referenced objects. That means the original and copied objects are completely independent.
Changes in the cloned object do not affect the original.
Example – Deep Copy
class Address {
String city;
Address(String city) {
this.city = city;
}
}
class Student implements Cloneable {
int id;
String name;
Address address;
Student(int id, String name, Address address) {
this.id = id;
this.name = name;
this.address = address;
}
public Object clone() throws CloneNotSupportedException {
Student s = (Student) super.clone();
s.address = new Address(address.city); // create new Address object
return s;
}
}
public class DeepCopyExample {
public static void main(String[] args) throws CloneNotSupportedException {
Address addr = new Address("Delhi");
Student s1 = new Student(101, "Aman", addr);
Student s2 = (Student) s1.clone();
s2.address.city = "Mumbai";
System.out.println("Original: " + s1.address.city);
System.out.println("Clone: " + s2.address.city);
}
}
Output:
Original: Delhi
Clone: Mumbai
Explanation:
In deep copy, a new Address object is created during cloning, so changes to s2 do not affect s1.
Key Differences Between Shallow and Deep Copy
| Feature | Shallow Copy | Deep Copy |
|---|---|---|
| Definition | Copies only the top-level object | Copies the object and all referenced objects |
| Nested Objects | Shared between original and clone | New copies created for nested objects |
| Memory Usage | Less memory | More memory |
| Performance | Faster | Slower |
| Independence | Dependent (changes reflect in both) | Independent |
| Implementation | super.clone() | Custom clone() + new object creation |
┌──────────────────────────────────┐
│ Original Object │
│ ┌────────────┐ │
│ │ Address → A │───┐ │
└──┴────────────┘ │
│
▼
┌────────────────┐
│ City = Delhi │
└────────────────┘
🔸 Shallow Copy
┌──────────────────────────────────┐
│ Copied Object │
│ ┌────────────┐ │
│ │ Address → A │───────┘ │
└──┴────────────┘
🔹 Deep Copy
┌──────────────────────────────────┐
│ Copied Object │
│ ┌────────────┐ │
│ │ Address → B │───┐ │
└──┴────────────┘ │
▼
┌────────────────┐
│ City = Delhi │
└────────────────┘Ways to Achieve Deep Copy in Java

-
Override
clone()properly (as shown above) -
Use Copy Constructors
class Student { int id; Address address; Student(Student s) { this.id = s.id; this.address = new Address(s.address.city); } } -
Serialization (for complex deep copying)
Serialize and then deserialize the object to get a full deep copy.
Example – Shallow vs Deep Copy Comparison
class Address {
String city;
Address(String city) { this.city = city; }
}
class Employee implements Cloneable {
String name;
Address address;
Employee(String name, Address address) {
this.name = name;
this.address = address;
}
// Shallow copy
public Employee shallowCopy() throws CloneNotSupportedException {
return (Employee) super.clone();
}
// Deep copy
public Employee deepCopy() throws CloneNotSupportedException {
Employee copy = (Employee) super.clone();
copy.address = new Address(address.city);
return copy;
}
}
public class CopyTest {
public static void main(String[] args) throws CloneNotSupportedException {
Employee e1 = new Employee("Ravi", new Address("Delhi"));
Employee e2 = e1.shallowCopy();
Employee e3 = e1.deepCopy();
e2.address.city = "Mumbai";
e3.address.city = "Chennai";
System.out.println("Original: " + e1.address.city);
System.out.println("Shallow Copy: " + e2.address.city);
System.out.println("Deep Copy: " + e3.address.city);
}
}
Output:
Original: Mumbai
Shallow Copy: Mumbai
Deep Copy: Chennai
When to Use
| Use Case | Recommended Copy |
|---|---|
| Simple objects without references | Shallow Copy |
| Complex/nested objects | Deep Copy |
| Large data (performance priority) | Shallow Copy |
| Critical data isolation (safety) | Deep Copy |
Points to Remember
clone()method inObjectperforms shallow copy by default.- To perform deep copy, manually clone referenced objects inside
clone(). - Always implement
Cloneableinterface when overridingclone(). - Alternatively, use copy constructors or serialization for cleaner deep copying.
