π 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)
π― Why This Matters
Section titled βπ― Why This Mattersβ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
πΉ What is Shallow Copy?
Section titled βπΉ What is Shallow Copy?β- 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
πΉ What is Deep Copy?
Section titled βπΉ What is Deep Copy?β- 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
π§ͺ Example Code
Section titled βπ§ͺ Example Codeβ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 + "}"; }}π Test Shallow vs Deep Copy
Section titled βπ Test Shallow vs Deep Copyβ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 }}Output:
Section titled βOutput:β=== 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'}