Shallow vs Deep Copy in Java – Cloning Objects Properly

Illustration for Shallow vs Deep Copy in Java – Cloning Objects Properly
By Last updated:

Introduction

Copying objects in Java is not as simple as assigning variables. Understanding shallow vs deep copy ensures that your objects behave correctly when duplicated.

Why It Matters

  • Prevents unintended data sharing between objects.
  • Avoids bugs when working with mutable objects.
  • Ensures proper object design in real-world projects.

When to Use

  • Shallow Copy: When you want a quick, low-cost duplicate and inner objects are immutable or don’t need separation.
  • Deep Copy: When you need a full independent copy, especially with mutable or nested objects.

Core Concepts

What is Shallow Copy?

  • Copies the top-level object only.
  • References of nested objects are shared, not cloned.
  • Typically implemented via clone() or copy constructors.
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
    protected Object clone() throws CloneNotSupportedException {
        return super.clone(); // Shallow copy
    }
}

What is Deep Copy?

  • Creates a new copy of all nested objects.
  • Ensures full independence of the clone.
@Override
protected Object clone() throws CloneNotSupportedException {
    Person cloned = (Person) super.clone();
    cloned.address = new Address(this.address.city); // Deep copy of nested object
    return cloned;
}

Real-World Analogy

  • Shallow Copy: Photocopying a folder but leaving all internal files as links to originals.
  • Deep Copy: Creating a new folder and duplicating every file inside.

Comparison Table

Feature Shallow Copy Deep Copy
Speed Faster Slower
Memory Usage Less More
Nested Objects Shared references Fully cloned
Use Case Immutable or simple objects Mutable, complex object graphs
Implementation clone(), copy constructor Manual clone, serialization, custom logic

Real-World Use Cases

  • Shallow Copy:
    • DTOs with immutable fields.
    • Lightweight duplication in caching.
  • Deep Copy:
    • Cloning configuration objects.
    • Game state snapshots.
    • Complex data structures.

Common Mistakes & Anti-Patterns

  1. Assuming clone() does deep copy by default:
    • Default implementation is always shallow.
  2. Copying mutable objects shallowly:
    • Leads to shared state bugs.
  3. Ignoring CloneNotSupportedException:
    • All classes must implement Cloneable to use clone().

Performance & Memory Implications

  • Shallow Copy:
    • Low overhead, fast.
    • Can cause hidden side effects if nested objects are modified.
  • Deep Copy:
    • Expensive in CPU and memory.
    • Useful for data isolation and thread safety.

Tuning Tips

  • Use deep copy only when necessary.
  • Consider immutability to avoid deep copy overhead.

Best Practices

  • Favor copy constructors or factory methods over clone().
  • Use libraries like Apache Commons SerializationUtils.clone() for deep copy.
  • Keep objects immutable to avoid needing deep copies.
  • Clearly document copy behavior.

Java Version Relevance

Version Change
Java 8 No direct changes, serialization-based cloning common
Java 9+ clone() discouraged in favor of copy methods
Java 14+ Records introduced (immutability reduces need for deep copy)

Code Example: Testing Shallow vs Deep Copy

public class CopyDemo {
    public static void main(String[] args) throws CloneNotSupportedException {
        Address address = new Address("New York");
        Person p1 = new Person("John", address);
        Person p2 = (Person) p1.clone(); // Shallow or deep depending on implementation

        p2.address.city = "Los Angeles";
        System.out.println(p1.address.city); // Shallow: Los Angeles, Deep: New York
    }
}

Conclusion & Key Takeaways

  • Shallow copies share nested object references, deep copies don’t.
  • Choose based on object mutability and performance needs.
  • Avoid default clone(); prefer copy constructors or immutability.
  • Deep copies ensure safety but cost performance.

FAQ

  1. Does Object.clone() perform a deep copy?
    No, it performs a shallow copy.

  2. When should I use deep copy?
    When nested objects are mutable and need full separation.

  3. Is serialization a way to deep copy?
    Yes, but slower compared to manual copy.

  4. What’s better: clone() or copy constructor?
    Copy constructors are safer and more flexible.

  5. Do immutable objects need deep copy?
    No, because they can’t change.

  6. What if my class has many nested objects?
    Implement custom deep copy logic or use libraries.

  7. Is deep copy thread-safe?
    It helps avoid shared state, improving thread safety.

  8. What’s the cost of deep copy?
    Higher CPU and memory usage.

  9. Can final fields be deep copied?
    Yes, if initialized with new objects in constructor.

  10. Does Java provide built-in deep copy?
    No, you must implement it manually or use third-party utilities.