Shallow copy vs Deep copy in Java
This post will discuss shallow copy and deep copy in Java in detail with examples.
Shallow Copy
In Java, java.lang.Object provides clone() method, which is widely used to create copy of the object. The default implementation Object.clone() method returns an exact copy of the original object. It does that by field-by-field assignment of primitive, mutable, and immutable types. In other words, Object.clone() creates a new object of the same run-time type as the original object, and for every primitive, mutable, and immutable field, it performs newObj.field = obj.field operation, where newObj and obj are the new object and original object, respectively.
This works fine if the class contains only primitives and immutable fields. But field-by-field assignment will not result in desired behavior if any mutable fields such as collections and arrays are present in the class since memory would be shared between the original and the copy. As referenced objects are shared, if one of either object is modified, the change is visible in the other. This is nothing but a shallow copy, where object references get copied and not the referred objects themselves.

|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Set; import java.util.Map; // `Subject` needs to implement `Cloneable` too!! class Subject implements Cloneable { private Set<String> subjects; public Subject() { subjects = new HashSet<>( Arrays.asList("Maths", "Science", "English", "History") ); } @Override public Object clone() throws CloneNotSupportedException { // call `super.clone()` to obtain the cloned object reference return super.clone(); } @Override public String toString() { return subjects.toString(); } public Set<String> getSubjects() { return subjects; } } // Note that the `Student` implements `Cloneable` interface class Student implements Cloneable { private String name; // immutable field private int age; // primitive field private Subject subjects; private Map<String, Integer> map; public Student(String name, int age) { this.name = name; this.age = age; map = new HashMap<String, Integer>() {{ put(name, age); }}; subjects = new Subject(); } @Override public String toString() { return Arrays.asList(name, String.valueOf(age), subjects.toString()).toString(); } @Override public Object clone() throws CloneNotSupportedException { // call `super.clone()` to obtain the cloned object reference return super.clone(); } public Set<String> getSubjects() { return subjects.getSubjects(); } public Map<String, Integer> getMap() { return map; } // include remaining getters and setters } // Demonstrate shallow copy of objects using the `clone()` method class Main { // Utility method to compare two objects. It prints shallow copy // if both objects share the same object; otherwise, it prints deep copy public static void compare(Object ob1, Object ob2) { if (ob1 == ob2) { System.out.println("Shallow Copy"); } else { System.out.println("Deep Copy"); } }; public static void main(String[] args) { Student student = new Student("Jon Snow", 22); Student clone = null; try { clone = (Student) student.clone(); System.out.println("Cloned Object: " + clone + '\n'); } catch (CloneNotSupportedException ex) { ex.printStackTrace(); } compare(student.getSubjects(), clone.getSubjects()); compare(student.getMap(), clone.getMap()); // any change made to the clone's map will reflect on the student's map clone.getMap().put("John Cena", 40); System.out.println(student.getMap()); } } |
Output:
Cloned Object: [Jon Snow, 22, [Maths, English, Science, History]]
Shallow Copy
Shallow Copy
{Jon Snow=22, John Cena=40}
Deep Copy
An alternative to shallow copy is a deep copy, where new objects are created for any referenced objects rather than references to objects being copied.
The JDK provides no deep-copy equivalent to Object.clone() method. But we can achieve deep copy by modifying the default implementation of the Object.clone() method and allocate new memory for mutable fields of the object returned by super.clone() before returning to the caller. If the object has any references to other objects as fields, it is recommended to call the clone() method on them. Primitive fields can be ignored, as their content is already copied. For immutable fields like string, we can let the method copy the reference, and both the original and its clone will share the same object. Now any changes made to the cloned object will not be reflected in an original object or vice-versa.

|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Set; import java.util.Map; // `Subject` needs to implement `Cloneable` too!! class Subject implements Cloneable { private Set<String> subjects; public Subject() { subjects = new HashSet<>( Arrays.asList("Maths", "Science", "English", "History") ); } @Override public Subject clone() throws CloneNotSupportedException { Subject obj = (Subject)super.clone(); obj.subjects = new HashSet<>(this.subjects); return obj; } @Override public String toString() { return subjects.toString(); } public Set<String> getSubjects() { return subjects; } } // Note that the `Student` implements `Cloneable` interface class Student implements Cloneable { private String name; // immutable field private int age; // primitive field private Subject subjects; private Map<String, Integer> map; public Student(String name, int age) { this.name = name; this.age = age; map = new HashMap<String, Integer>() {{ put(name, age); }}; subjects = new Subject(); } @Override public String toString() { return Arrays.asList(name, String.valueOf(age), subjects.toString()).toString(); } @Override public Object clone() throws CloneNotSupportedException { Student student = (Student) super.clone(); // primitive fields like int are not copied, as their content // is already copied // String is immutable // call `clone()` on `Subject` object student.subjects = this.subjects.clone(); // create a new instance of `Hashmap` student.map = new HashMap<>(this.map); return student; } public Set<String> getSubjects() { return subjects.getSubjects(); } public Map<String, Integer> getMap() { return map; } // include remaining getters and setters } // Demonstrate a deep copy of objects using the `clone()` method class Main { // Utility method to compare two objects. It prints shallow copy // if both objects share the same object; otherwise, it prints deep copy public static void compare(Object ob1, Object ob2) { if (ob1 == ob2) { System.out.println("Shallow Copy"); } else { System.out.println("Deep Copy"); } }; public static void main(String[] args) { Student student = new Student("Jon Snow", 22); Student clone = null; try { clone = (Student) student.clone(); System.out.println("Cloned Object: " + clone + '\n'); } catch (CloneNotSupportedException ex) { ex.printStackTrace(); } compare(student.getSubjects(), clone.getSubjects()); compare(student.getMap(), clone.getMap()); // any change made to the clone's map will not reflect on the student's map clone.getMap().put("John Cena", 40); System.out.println(student.getMap()); } } |
Output:
Cloned Object: [Jon Snow, 22, [Maths, English, Science, History]]
Deep Copy
Deep Copy
{Jon Snow=22}
The Object.clone() method will result in a compilation error if we try to assign a value to a final field. The solution to address this problem is to use serialization and deserialization, which is relatively easier to implement Object.clone() as a deep copy can be several levels deep and error-prone for complex graph objects if done manually. Apache Commons Lang also provides an implementation of the clone() method. It also performs deep cloning using serialization. The implementation can be seen here. Please note that Serialization will be very slow and does not clone transient fields. We can also use Copy Constructor if the object is not too complex.
Differences between Shallow Copy and Deep Copy
Let’s wrap this article by listing a few major differences between Shallow Copy and Deep Copy of objects in Java:
- Shallow copy is a bitwise copy of an object and works fine if the class contains only primitives and immutable fields. But field-by-field assignment will cause references (memory address) to mutable fields/objects being copied. A deep copy will create a distinct copy for each of the object’s mutable fields and referenced objects, rather than references to objects being copied.
- The referenced objects are shared in the shallow copy. So if one of either object is modified, the change is visible in the other. On the other hand, new objects are created for any referenced objects in the deep copy. So if one of either object is modified, the change is NOT visible in the other.
- Deep copy can be many times slower than the shallow copy. Shallow copy involves copying only from one level of an object, while deep cloning involves recursively copying all mutable types (involve multiple levels) that can have a significant impact on performance.
- Shallow copy is preferred if an object consists of only primitive and immutable fields. A deep copy is a preferable approach over a shallow copy when an object has any referenced objects present in it.
- Deep copy is tedious to implement, error-prone, and difficult to maintain. The code must be modified every time any change is made to the class fields, unlike a shallow copy implementation.
- Default implementation of
Object.clone()creates the shallow copy of an object. To create the deep copy of an object, we need to modify mutable fields of the object returned bysuper.clone()before returning to the caller.
That’s all about shallow copy and deep copy in Java.
References:
https://en.wikipedia.org/wiki/Object_copying#Copying_in_Java
https://en.wikipedia.org/wiki/Clone_(Java_method)
Thanks for reading.
To share your code in the comments, please use our online compiler that supports C, C++, Java, Python, JavaScript, C#, PHP, and many more popular programming languages.
Like us? Refer us to your friends and support our growth. Happy coding :)