Skip to content
Dev Dump

πŸŒ€ Shallow Copy vs Deep Copy in Java

When you copy an object in Java, you have two choices:

  • Shallow Copy β€” copies the object but shares references of nested objects
  • Deep Copy β€” copies the object and everything inside it (all nested objects)

Understanding the difference between shallow and deep copying is crucial for:

  • Object lifecycle management
  • Preventing unintended side effects
  • Designing robust data structures
  • Interview success - this is a very common question
  • Debugging object reference issues
  • Copies the top-level object
  • Nested objects are not copied, but referenced (shared)
  • Changes in nested objects reflect in both original and copy
  • Faster and memory efficient
  • Riskier due to shared references
  • Copies the object and all objects it references recursively
  • Completely independent copy β€” changes in the copy don’t affect the original
  • Slower and memory intensive
  • Safer due to complete isolation
  • More complex to implement

Let’s define a simple Person class with an Address object inside:

class Address {
String city;
String street;
Address(String city, String street) {
this.city = city;
this.street = street;
}
@Override
public String toString() {
return "Address{city='" + city + "', street='" + street + "'}";
}
}
class Person implements Cloneable {
String name;
int age;
Address address;
Person(String name, int age, Address address) {
this.name = name;
this.age = age;
this.address = address;
}
// Shallow copy using clone()
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone(); // Copies Person, but address is shared
}
// Deep copy method
protected Person deepClone() {
Address newAddress = new Address(address.city, address.street);
return new Person(name, age, newAddress);
}
// Custom deep copy with more control
protected Person customDeepClone() {
try {
Person cloned = (Person) super.clone();
// Deep copy the address
cloned.address = new Address(address.city, address.street);
return cloned;
} catch (CloneNotSupportedException e) {
throw new RuntimeException("Clone not supported", e);
}
}
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + ", address=" + address + "}";
}
}

public class CopyTest {
public static void main(String[] args) throws CloneNotSupportedException {
Address addr = new Address("New York", "Broadway");
Person p1 = new Person("Alice", 25, addr);
System.out.println("=== Original Person ===");
System.out.println("p1: " + p1);
// Shallow copy
Person p2 = (Person) p1.clone();
System.out.println("\n=== After Shallow Copy ===");
System.out.println("p2: " + p2);
// Deep copy
Person p3 = p1.deepClone();
System.out.println("\n=== After Deep Copy ===");
System.out.println("p3: " + p3);
// Change the address in p1
System.out.println("\n=== Changing Address in p1 ===");
p1.address.city = "Los Angeles";
p1.address.street = "Hollywood Blvd";
System.out.println("p1 address: " + p1.address);
System.out.println("p2 address (shallow): " + p2.address); // Changed! (shared reference)
System.out.println("p3 address (deep): " + p3.address); // Unchanged! (independent)
// Change primitive fields
System.out.println("\n=== Changing Primitive Fields in p1 ===");
p1.name = "Alice Smith";
p1.age = 26;
System.out.println("p1: " + p1);
System.out.println("p2: " + p2); // Primitives are copied, so unchanged
System.out.println("p3: " + p3); // Primitives are copied, so unchanged
}
}
=== Original Person ===
p1: Person{name='Alice', age=25, address=Address{city='New York', street='Broadway'}}
=== After Shallow Copy ===
p2: Person{name='Alice', age=25, address=Address{city='New York', street='Broadway'}}
=== After Deep Copy ===
p3: Person{name='Alice', age=25, address=Address{city='New York', street='Broadway'}}
=== Changing Address in p1 ===
p1 address: Address{city='Los Angeles', street='Hollywood Blvd'}
p2 address (shallow): Address{city='Los Angeles', street='Hollywood Blvd'}
p3 address (deep): Address{city='New York', street='Broadway'}
=== Changing Primitive Fields in p1 ===
p1: Person{name='Alice Smith', age=26, address=Address{city='Los Angeles', street='Hollywood Blvd'}}
p2: Person{name='Alice', age=25, address=Address{city='Los Angeles', street='Hollywood Blvd'}
p3: Person{name='Alice', age=25, address=Address{city='New York', street='Broadway'}