Hurry! Try our new Interactive Courses for FREE. 🥳   🚀

How to Make a Deep Copy of an Object in Java?

When copying objects in Java, we may end up with a shallow copy and making any modifications to the cloned object will also alter the original object. To avoid this, we use the concept of Deep Copy. When this technique is used, then the copied object is not dependent on the original and we can work on both the objects independently.

What is Shallow Copy?

A shallow copy will simply copy the values of the fields of the original object into the new object. In shallow copy, if the original object has any references to other class objects(a class member is itself an object of some other class), then only the reference of that object is cloned. Therefore, any changes in this referenced object will be reflected on both the original and the cloned object.

Shallow Copy

For example, suppose, we have a Student class that has a member variable of type GPA(user-defined class). Now, let's create an object of the Student class called s. Next, we will create a clone of this object and call it a copy. Now, if we make any changes to the GPA of the copy object, then this change is also reflected in the GPA field of the original s object. This is an example of shallow cloning.

Remember, we need to implement the Cloneable interface and override the clone() method to allow cloning. We will simply use the standard clone() method to perform shallow cloning. The standard clone() method is shown below.

//Standard clone() method to perform Shallow Cloning

@Override
protected Object clone() throws CloneNotSupportedException {
    return super.clone();
}

Example: Swallow Copy in Java

The complete example of shallow cloning is demonstrated by the code below.

class GPA
{
	int firstYear;
	int secondYear;

	GPA(int fy, int sy)
	{
		this.firstYear = fy;
		this.secondYear = sy;
	}

	public int getFirstYear() {
		return firstYear;
	}

	public void setFirstYear(int firstYear) {
		this.firstYear = firstYear;
	}

	public int getSecondYear() {
		return secondYear;
	}

	public void setSecondYear(int secondYear) {
		this.secondYear = secondYear;
	}
}

class Student implements Cloneable
{
	private String name;
	private GPA gpa;
	
	Student(String name, GPA gpa)
	{
		this.name = name;
		this.gpa = gpa;
	}
	
    //Standard clone() method to perform Shallow Cloning
	@Override
	protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
	
	public String getName()
	{
		return this.name;
	}

	public GPA getGPA()
	{
		return this.gpa;
	}
	public void setName(String name)
	{
		this.name = name;
	}	
	public void setGPA(GPA g)
	{
		this.gpa = g;
	}
}

public class CopyDemo
{
	public static void main(String[] args) throws CloneNotSupportedException
	{
		GPA g = new GPA(7, 8);
		Student s = new Student("Justin", g);//original object
		
		Student copy = (Student)s.clone();//shallow copy
		
		System.out.println("Original Object's GPA: " + s.getGPA().getFirstYear() + " " + s.getGPA().getSecondYear());
		System.out.println("Cloned Object's GPA: " + copy.getGPA().getFirstYear() + " " + copy.getGPA().getSecondYear());
		
		copy.getGPA().setFirstYear(10);//Changing the GPA field of the shallow copy
		
		System.out.println("\nAfter changing the shallow copy");
		System.out.println("Original Object's GPA: " + s.getGPA().getFirstYear() + " " + s.getGPA().getSecondYear());
		System.out.println("Cloned Object's GPA: " + copy.getGPA().getFirstYear() + " " + copy.getGPA().getSecondYear());
		
	}
}


Original Object's GPA: 7 8
Cloned Object's GPA: 7 8

After changing the shallow copy
Original Object's GPA: 10 8
Cloned Object's GPA: 10 8

What is Deep Copy?

A deep copy will create a new object for the referenced objects by the original class. This ensures that the original object and the cloned object are independent. For primitive data types, shallow cloning and deep cloning work in the same way as no references are involved.

Deep Copy

Making a Deep Copy by overriding the clone() method

In the shallow cloning example, we used the standard clone() method but if we wish to create a deep copy then we must make some changes to this method.

We will basically clone the referenced class and since the referenced class just contains primitive types, so a deep copy is created. We then assign this deep copy to a new object that we created in the clone() method and simply return it. The modified clone() method is shown below.

@Override
protected Object clone() throws CloneNotSupportedException
{
    Student s = (Student)super.clone();
    s.setGPA((GPA)s.getGPA().clone());
    return s;
}

We also need to override the clone() method in the referenced class(GPA in this case) to make sure that we can use the clone() method on it.

class GPA implements Cloneable
{
	int firstYear;
	int secondYear;
	
	GPA(int fy, int sy)
	{
		this.firstYear = fy;
		this.secondYear = sy;
	}
	
	@Override
	protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    //getters and setters
}

Example: Deep Copy in Java

The complete code is shown below. As we can see in the output, changing something in the copied object does not modify the original one.

class GPA implements Cloneable
{
	int firstYear;
	int secondYear;
	
	GPA(int fy, int sy)
	{
		this.firstYear = fy;
		this.secondYear = sy;
	}
	
	@Override
	protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

	public int getFirstYear() {
		return firstYear;
	}

	public void setFirstYear(int firstYear) {
		this.firstYear = firstYear;
	}

	public int getSecondYear() {
		return secondYear;
	}

	public void setSecondYear(int secondYear) {
		this.secondYear = secondYear;
	}
}

class Student implements Cloneable
{
	private String name;
	private GPA gpa;
	
	Student(String name, GPA gpa)
	{
		this.name = name;
		this.gpa = gpa;
	}
	
	@Override
	protected Object clone() throws CloneNotSupportedException {
        Student s = (Student)super.clone();
        s.setGPA((GPA)s.getGPA().clone());
        return s;
    }
	
	public String getName()
	{
		return this.name;
	}

	public GPA getGPA()
	{
		return this.gpa;
	}
	public void setName(String name)
	{
		this.name = name;
	}	
	public void setGPA(GPA g)
	{
		this.gpa = g;
	}
}

public class CopyDemo
{
	public static void main(String[] args) throws CloneNotSupportedException
	{
		GPA g = new GPA(7, 8);
		Student s = new Student("Justin", g);//original object
		
		Student copy = (Student)s.clone();//deep copy
		
		System.out.println("Original Object's GPA: " + s.getGPA().getFirstYear() + " " + s.getGPA().getSecondYear());
		System.out.println("Cloned Object's GPA: " + copy.getGPA().getFirstYear() + " " + copy.getGPA().getSecondYear());
		
		copy.getGPA().setFirstYear(10);//Changing the GPA field of the deep copy
		
		System.out.println("\nAfter changing the Deep copy");
		System.out.println("Original Object's GPA: " + s.getGPA().getFirstYear() + " " + s.getGPA().getSecondYear());
		System.out.println("Cloned Object's GPA: " + copy.getGPA().getFirstYear() + " " + copy.getGPA().getSecondYear());
		
	}
}


Original Object's GPA: 7 8
Cloned Object's GPA: 7 8

After changing the Deep copy
Original Object's GPA: 7 8
Cloned Object's GPA: 10 8

Using Copy Constructors in Java

Copy Constructors are the easiest way to make a deep copy. We simply create a constructor which takes an object of the same class as input and returns a new object with the same values but new references(if referenced classes are involved).

The copy constructor of the GPA class is shown below.

GPA(GPA g)
{
	this.firstYear = g.firstYear;
	this.secondYear = g.secondYear;
}

The copy constructor of the Student class is shown below.

Student(Student s)
{
	this.name = new String(s.name);
	this.gpa = new GPA(s.gpa);
}
	

Example: Copy Constructor in Java

The complete code will look like the following. As we can see, modifying the copied object does not alter the original object.

class GPA
{
	int firstYear;
	int secondYear;
	
	GPA(int fy, int sy)
	{
		this.firstYear = fy;
		this.secondYear = sy;
	}
	
	GPA(GPA g)
	{
		this.firstYear = g.firstYear;
		this.secondYear = g.secondYear;
	}
	

	public int getFirstYear() {
		return firstYear;
	}

	public void setFirstYear(int firstYear) {
		this.firstYear = firstYear;
	}

	public int getSecondYear() {
		return secondYear;
	}

	public void setSecondYear(int secondYear) {
		this.secondYear = secondYear;
	}
}

class Student
{
	private String name;
	private GPA gpa;
	
	Student(String name, GPA gpa)
	{
		this.name = name;
		this.gpa = gpa;
	}
	
	Student(Student s)
	{
		this.name = new String(s.name);
		this.gpa = new GPA(s.gpa);
	}
	

	public String getName()
	{
		return this.name;
	}

	public GPA getGPA()
	{
		return this.gpa;
	}
	public void setName(String name)
	{
		this.name = name;
	}	
	public void setGPA(GPA g)
	{
		this.gpa = g;
	}
}

public class CopyDemo
{
	public static void main(String[] args) throws CloneNotSupportedException
	{
		
		GPA g = new GPA(7, 8);
		Student s = new Student("Justin", g);//Original Object
		
		Student copy = new Student(s);//Deep copy
		
		System.out.println("Original Object's GPA: " + s.getGPA().getFirstYear() + " " + s.getGPA().getSecondYear());
		System.out.println("Cloned Object's GPA: " + copy.getGPA().getFirstYear() + " " + copy.getGPA().getSecondYear());
		
		copy.getGPA().setFirstYear(10);//Changing the GPA field of the deep copy
		
		System.out.println("\nAfter changing the Deep copy");
		System.out.println("Original Object's GPA: " + s.getGPA().getFirstYear() + " " + s.getGPA().getSecondYear());
		System.out.println("Cloned Object's GPA: " + copy.getGPA().getFirstYear() + " " + copy.getGPA().getSecondYear());
	}
}


Original Object's GPA: 7 8
Cloned Object's GPA: 7 8

After changing the Deep copy
Original Object's GPA: 7 8
Cloned Object's GPA: 10 8

Deep Copy using Serialization

Serialization is another simple way of performing a deep copy. Serializing an object and then deserializing it essentially produces a new object with new references but the same values. This will return a new object which is a deep copy of the original one. The object classes that need to be cloned must implement the Serializable interface. We will follow the below steps to create a deep copy using serialization.

  • First, create input and output streams and then, use them to create object input and object output stream.
  • The object to be cloned is passed to the object output stream.
  • A new object is read by using the object input stream. This will be the deep copy of the original object.

Serialization is not always the preferred way of making a deep copy as it is a lot more expensive than the clone() method and not all classes are serializable. The complete code is shown below.

The deep copy method in the Student class using serialization is shown below.

public Student deepCopyUsingSerialization()
{
	try
	{
		ByteArrayOutputStream bo = new ByteArrayOutputStream();
		ObjectOutputStream o = new ObjectOutputStream(bo);
		o.writeObject(this);
			
		ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
		ObjectInputStream i = new ObjectInputStream(bi);
			
		return (Student)i.readObject();
	}
	catch(Exception e)
	{
	    return null;
	}	
}

Example: Deep Copy using Serialization

The complete code is shown below.

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

@SuppressWarnings("serial")
class GPA implements Serializable 
{
	int firstYear;
	int secondYear;
	
	GPA(int fy, int sy)
	{
		this.firstYear = fy;
		this.secondYear = sy;
	}

	public int getFirstYear() {
		return firstYear;
	}

	public void setFirstYear(int firstYear) {
		this.firstYear = firstYear;
	}

	public int getSecondYear() {
		return secondYear;
	}

	public void setSecondYear(int secondYear) {
		this.secondYear = secondYear;
	}
}

@SuppressWarnings("serial")
class Student implements Serializable
{
	private String name;
	private GPA gpa;
	
	Student(String name, GPA gpa)
	{
		this.name = name;
		this.gpa = gpa;
	}
	
	public Student deepCopyUsingSerialization()
	{
		try
		{
			ByteArrayOutputStream bo = new ByteArrayOutputStream();
			ObjectOutputStream o = new ObjectOutputStream(bo);
			o.writeObject(this);
			
			ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
			ObjectInputStream i = new ObjectInputStream(bi);
			
			return (Student)i.readObject();
		}
		catch(Exception e)
		{
			return null;
		}	
	}

	public String getName()
	{
		return this.name;
	}

	public GPA getGPA()
	{
		return this.gpa;
	}
	public void setName(String name)
	{
		this.name = name;
	}	
	public void setGPA(GPA g)
	{
		this.gpa = g;
	}
}

public class CopyDemo
{
	public static void main(String[] args) throws CloneNotSupportedException
	{
		//deep cloning
		GPA g = new GPA(7, 8);
		Student s = new Student("Justin", g);
		
		Student copy = s.deepCopyUsingSerialization();
		
		System.out.println("Original Object's GPA: " + s.getGPA().getFirstYear() + " " + s.getGPA().getSecondYear());
		System.out.println("Cloned Object's GPA: " + copy.getGPA().getFirstYear() + " " + copy.getGPA().getSecondYear());
		
		copy.getGPA().setFirstYear(10);//Changing the GPA field of the deep copy
		
		System.out.println("\nAfter changing the Deep copy");
		System.out.println("Original Object's GPA: " + s.getGPA().getFirstYear() + " " + s.getGPA().getSecondYear());
		System.out.println("Cloned Object's GPA: " + copy.getGPA().getFirstYear() + " " + copy.getGPA().getSecondYear());
	}
}


Original Object's GPA: 7 8
Cloned Object's GPA: 7 8

After changing the Deep copy
Original Object's GPA: 7 8
Cloned Object's GPA: 10 8

Summary

A shallow copy will just copy the field values from the original object into the new object but we may get unexcepted results if the original object contains references to other objects. To solve this problem we use Deep Copy. A shallow copy and a deep copy will give the same results if the object only uses primitive fields. We can make a deep copy of an object by using the Cloneable() interface and overriding the clone() method. Copy constructors are an easier and more flexible way of performing a deep copy. We can also use Serialization but we need to remember that its computation is expensive than other methods.