Mastering Java Deep Copy: Techniques, Code Samples, and Best Practices

This article explains Java deep copy, its differences from shallow copy, key use cases such as data safety and avoiding shared state, and demonstrates three implementation methods—using Cloneable, serialization, and third‑party libraries like Guava—complete with runnable code examples.

Architect Chen
Architect Chen
Architect Chen
Mastering Java Deep Copy: Techniques, Code Samples, and Best Practices

What is Deep Copy in Java

Deep copy creates a completely independent copy of an object by recursively duplicating all objects reachable from the original, unlike shallow copy which copies only the top‑level fields.

Why Use Deep Copy

Data isolation : Modifications to the copy do not affect the original, protecting sensitive data.

Thread safety : Each thread can work with its own copy, avoiding shared mutable state.

Complex object graphs : Nested objects, collections, and graphs are fully duplicated.

Testing and backup : Provides reliable clones for experiments, backups, or unit tests.

Common Implementation Techniques

1. Using Cloneable and overriding clone()

Implement Cloneable, call super.clone() for a shallow copy, then manually deep‑copy mutable fields (e.g., collections) by creating new instances.

import java.util.ArrayList;
import java.util.List;

class Person implements Cloneable {
    private String name;
    private List<String> hobbies;

    public Person(String name, List<String> hobbies) {
        this.name = name;
        this.hobbies = hobbies;
    }

    public void addHobby(String hobby) {
        hobbies.add(hobby);
    }

    @Override
    public Person clone() throws CloneNotSupportedException {
        // shallow copy
        Person copy = (Person) super.clone();
        // deep copy mutable list
        copy.hobbies = new ArrayList<>(this.hobbies);
        return copy;
    }

    @Override
    public String toString() {
        return "Person{name='" + name + "', hobbies=" + hobbies + "}";
    }
}

public class Main {
    public static void main(String[] args) throws CloneNotSupportedException {
        List<String> hobbies = new ArrayList<>();
        hobbies.add("Reading");
        hobbies.add("Swimming");

        Person p1 = new Person("Alice", hobbies);
        Person p2 = p1.clone();
        p2.addHobby("Hiking");

        System.out.println("p1 = " + p1);
        System.out.println("p2 = " + p2);
    }
}

2. Using Java Serialization

Serializing an object to a byte array and deserializing it creates a new object graph. All classes in the graph must implement java.io.Serializable.

import java.io.*;

class DeepCopyViaSerialization implements Serializable {
    public Object deepCopy() throws IOException, ClassNotFoundException {
        // Serialize to byte array
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try (ObjectOutputStream out = new ObjectOutputStream(bos)) {
            out.writeObject(this);
        }

        // Deserialize from byte array
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        try (ObjectInputStream in = new ObjectInputStream(bis)) {
            return in.readObject();
        }
    }
}

3. Using Third‑Party Libraries

Libraries such as Google Guava provide immutable collection factories that effectively act as deep copies because the returned collections cannot be modified.

import com.google.common.collect.ImmutableList;

public class DeepCopyWithGuava {
    public static void main(String[] args) {
        // Original immutable list
        ImmutableList<String> original = ImmutableList.of("A", "B", "C");

        // Deep copy – creates a new immutable instance
        ImmutableList<String> copy = ImmutableList.copyOf(original);

        // Reassign original reference; copy remains unchanged
        original = ImmutableList.of("X", "Y", "Z");

        System.out.println("Original: " + original);
        System.out.println("Copy: " + copy);
    }
}

Considerations

Cloneable requires explicit handling of each mutable field; forgetting a field results in a shallow copy.

Serialization can be slower and requires all classes to be serializable; it cannot copy objects that hold transient resources such as open streams.

Third‑party utilities often work only for immutable data structures; mutable objects still need custom cloning.

serializationguavadeep copyCloneable
Architect Chen
Written by

Architect Chen

Sharing over a decade of architecture experience from Baidu, Alibaba, and Tencent.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.