Deep, Shallow and Lazy Copy with Java Examples

Last Updated : 23 Jan, 2026

In object-oriented programming, object copying refers to creating a new object using an existing object’s data. In Java, this is commonly done using constructors, the clone() method, or custom logic, and is closely related to cloning and reference handling.

Based on how data is copied, object copying is classified into:

1. Shallow Copy

A shallow copy creates a new object but does not duplicate referenced objects; instead, it copies their references. As a result, both the original and copied objects point to the same memory location for reference fields.

  • Default behavior of the Object.clone() method.
  • Primitive fields are copied, while reference fields are shared.
  • Fast and memory-efficient since no deep object creation occurs.
  • Risk of unintended side effects due to shared references.
  • Changes made through one object are reflected in all copies sharing the reference.
Java
import java.util.Arrays;

class Ex {
    private int[] data;
    // Shallow copy
    public Ex(int[] values) {
        this.data = values;
    }
    public void showData() {
        System.out.println(Arrays.toString(data));
    }
}

Explanation:

  • In the constructor Ex(int[] values), the statement this.data = values; assigns the same array reference to data.
  • No new array is created, so both data and vals point to the same memory location.
  • When vals[0] is modified in UsesEx, the change is reflected in data, producing the updated output.
     
shallow_copy
Shallow copy representation

This can lead to unpleasant side effects if the elements of values are changed via some other reference.

Java
class UsesEx {
    public static void main(String[] args) {
        int[] vals = {3, 7, 9};
        Ex e = new Ex(vals);

        e.showData();   // [3, 7, 9]

        vals[0] = 13;
        e.showData();   // [13, 7, 9]
    }
}

Output:

[3, 7, 9]
[13, 7, 9]

Explanation: Both vals and data refer to the same array. Any modification through one reference affects the other.

2. Deep Copy 

A deep copy creates a new object along with separate copies of all referenced objects, ensuring complete independence between the original and copied objects. Any change made to one object does not affect the other.

  • Achieved by writing custom copying logic.
  • When cloning is used, all referenced objects must also be cloned.
  • Provides a safe and fully independent copy of the object.
  • Requires higher memory usage compared to shallow copy.
  • More complex to implement due to recursive copying of references.
Java
import java.util.Arrays;

class Ex {
    private int[] data;

    // Deep copy
    public Ex(int[] values) {
        data = new int[values.length];
        for (int i = 0; i < values.length; i++) {
            data[i] = values[i];
        }
    }
    public void showData() {
        System.out.println(Arrays.toString(data));
    }
}

Explanation:

  • Inside the constructor, a new array is created using new int[values.length].
  • A for loop copies each element from values into the new data array.
  • Modifying vals[0] in UsesEx does not affect data because both arrays refer to different memory locations.
deep_copy
Deep copy represenation
Java
class UsesEx {
    public static void main(String[] args) {
        int[] vals = {3, 7, 9};
        Ex e = new Ex(vals);

        e.showData();   // [3, 7, 9]
        vals[0] = 13;
        e.showData();   // [3, 7, 9]
    }
}

Output:

[3, 7, 9]
[3, 7, 9]

Explanation: A new array is created and values are copied individually. Changes to vals do not affect data.

3. Lazy Copy 

Lazy copy is a hybrid approach of shallow copy and deep copy. Initially, multiple objects share the same data using a shallow copy. A deep copy is created only when a write operation is performed on any one of the objects. This technique is also known as copy-on-write.

  • Uses shallow copy during object creation
  • Maintains a reference count for shared data
  • Performs deep copy only when modification is required
Java
class SharedData {
    int[] data;
    int refCount;

    SharedData(int[] values) {
        data = values;
        refCount = 1;
    }
}
class LazyCopy {
    private SharedData shared;

    // Constructor
    LazyCopy(int[] values) {
        shared = new SharedData(values);
    }

    // Shallow copy
    LazyCopy(LazyCopy other) {
        this.shared = other.shared;
        this.shared.refCount++;
    }
    // Copy-on-write
    void setValue(int index, int value) {
        if (shared.refCount > 1) {
            shared.refCount--;
            shared = new SharedData(shared.data.clone());
        }
        shared.data[index] = value;
    }
    void showData() {
        for (int x : shared.data) {
            System.out.print(x + " ");
        }
        System.out.println();
    }
}

Explanation:

  • In the copy constructor LazyCopy(LazyCopy other), the shared reference is copied and refCount is incremented.
  • When setValue() is called, the condition shared.refCount > 1 triggers creation of a new copy using shared.data.clone().
  • The modification is applied only to the new copy, so obj1 remains unchanged while obj2 reflects the update.
values
Lazy copy representation
Java
public class Main {
    public static void main(String[] args) {
        int[] values = {3, 7, 9};

        LazyCopy obj1 = new LazyCopy(values);
        LazyCopy obj2 = new LazyCopy(obj1);

        obj2.setValue(2, 10);
        obj1.showData(); // 3 7 9
        obj2.showData(); // 3 7 10
    }
}

Output:

3 7 9
3 7 10

Explanation:

  • obj1 and obj2 initially share the same data
  • On modification, obj2 creates its own copy
  • Changes do not affect the original object

When to Use Which?

Scenario

Recommended Copy

Only primitive fields

Shallow Copy

Mutable reference fields

Deep Copy

Performance-critical systems

Lazy Copy

Immutable objects

Shallow Copy

Note:

  • Shallow copy is faster but unsafe for mutable objects
  • Deep copy ensures isolation but costs memory
  • Lazy copy balances performance and safety
Comment