Object cloning in java using clone method with example

What is a clone
A clone is an exact copy of something. Object cloning is required when you want to create a new object with the same values of properties and containing all methods as an existing object.
A practical scenario would be when creating a new entity whose most of the property values are common as all other entities except for a couple of properties. Instead of copying each property value one by one, you can clone the object and then change the properties having different values.

This article will explain how to make a clone of an object along with the concepts of shallow and deep cloning in java.
Cloning an object in java
Java provides a simple mechanism to create a copy of an object using clone() method. This method is defined in java.lang.Object class and has a protected access specifier.
For making a clone of an object, you need to perform the following steps:

  1. Make the class(whose objects need to be cloned) implement java.lang.Cloneable interface.
  2. Override clone method of java.lang.Object class. In the overridden method, you can write custom cloning functionality or call super.clone() in case you want the default behavior.

Clone example in java
Below is a sample class whose objects we will be cloning.

public class Student implements Cloneable {
	
   private String name;

   private int age;
	
   public Object clone() throws CloneNotSupportedException {
	return super.clone();
   }

    // Getter and setter methods
}

Example of how to clone an object of this class follows.

public class CloningDemo {
   public static void main(String[] args) {
      // create an object of student
      Student student = new Student();
      // set it values
      student.setAge(15);
      student.setName("John");
      try {
	 System.out.println("Creating clone...");
         // create a copy object
         Student clone = (Student)student.clone();
         // check if the objects are same
	 System.out.println("Are objects same? " +student.equals(clone));
	 System.out.println("Name = " + clone.getName());
	 System.out.println("Age = " + clone.getAge()); 
      } catch (CloneNotSupportedException e) {
         e.printStackTrace();
      }
   }
}

Above program creates a Student object and then creates its copy by invoking clone() method. It checks if the original and cloned objects are same by comparing them with equals() method. Finally it prints the values of fields of the new copy object.

Below is the output

Creating clone…
Are objects same? false
Name = John
Age = 15

which shows that both the objects are different and also the values of fields of new object are the same as the original object.
Few important points to remember about the above discussion are

  • java.lang.Cloneable interface is a java marker interface meaning that it has no methods.
  • If the class does not implement java.lang.Cloneable interface and you try to clone its objects, then a java.lang.CloneNotSupportedException is thrown.
  • clone() method performs shallow copy or shallow cloning by default and not deep cloning. Both these terms are explained next.

Shallow cloning or shallow copy in java
Java clone() method performs shallow copy by default. This means that it copies only primitive field values for the cloned object. Example of primitive field values are int, char, long, float, double etc.
If the object being cloned contains another object as a field(or instance variable), then it will copy the same object into the cloned object.
To demonstrate it with an example, let us modify our Student class to contain another object field.

public class Student implements Cloneable {
	
   private String name;

   private int age;
 
   /*
    * Non-primitive or Object instance variable
    */
   private Address address;
	
   public Object clone() throws CloneNotSupportedException {
	return super.clone();
   }

   // Getter and setter methods
}

where address is a field of an object type and its class is defined below.

public class Address {
   private String country;
	
   private String state;
	
   private String city;

   // Getter and setter methods
}

Now after taking clone of a student object, when we check the reference of address field of both the objects using statement

student.getAddress().equals(clone.getAddress())

it returns true which means that the address field of both the objects are the same.

This means that after cloning, if you change any field of address for the original object, then it will also modify the address field of cloned object and vice-versa. You can try it and see the result yourself.
Remember that it is not only applicable to custom objects but also to all java objects such as List, Map, Set etc. But with immutable objects such as String, Integer, Double etc., there is no problem since if you modify an immutable object, a new one is automatically created when it is modified.
Deep cloning or deep copy in java
In the previous section, we saw that if the cloned object contains another object as a field or instance variable, then a copy of this object is not created inside the new clone object.
In that case, if you modify this object for the clone, then the original object is also modified, which is not the desired behavior and the reason for this behavior is that clone method performs shallow copy and not deep copy.
Deep copy means that if a copy of some object is created, then a copy of nested objects should also be created so that both objects are independent of each other.
There are many ways in which deep cloning of an object can be done in java.
Method 1: Manual cloning
In this method, you need to modify the clone() method of original or parent object. In the clone() method of parent object, create a clone of all the nested objects as shown below.

public Object clone() throws CloneNotSupportedException {
   Student clone = (Student)super.clone();
   // get address of clone object
   Address originalAddress = clone.getAddress();
   // create a clone of address
   Address clonedAddress = (Address)originalAddress.clone();
   // set clone of address in the cloned object
   clone.setAddress(clonedAddress);
   return clone;
}

clone() method of Student creates a clone of its nested address object also and populates the new address in the cloned student object.
Above code can be replaced with a one-liner as

Student clone = (Student)super.clone();
clone.setAddress((Address)clone.getAddress().clone());

Let us test this using below program.

public class CloningDemo {
   public static void main(String[] args) {
      // create a student object
      Student student = new Student();
      student.setAge(15);
      student.setName("John");
      // create its address
      Address address = new Address();
      address.setCountry("India");
      address.setState("New Delhi");
      address.setCity("New Delhi");
      student.setAddress(address);
      try {
	// clone student
	Student clone = (Student)student.clone();
	// get original address
	Address originalAddress = student.getAddress();
	// get address of cloned object
	Address clonedAddress = clone.getAddress();
	// check if they are equal
	System.out.println("Are address objects same? "
                     + originalAddress.equals(clonedAddress));
	// change clone address country
	clonedAddress.setCountry("Australia");
	System.out.println("Original country: " + originalAddress.getCountry());
	System.out.println("Clone country: " + clonedAddress.getCountry());
      } catch (CloneNotSupportedException e) {
	e.printStackTrace();
      }
   }
}

Address objects from original and cloned student are compared and then the country of cloned address is changed.
Both the address objects are then compared to check if they are same and the countries of both addresses are printed. Below is the output

Are address objects same? false
Original country: India
Clone country: Australia

Look, both objects are different and modifying one address has no effect on the second.
For this method to work, the Address class should also implement java.lang.Cloneable interface and clone() method else a java.lang.CloneNotSupportedException will be raised.
This is because we are calling clone() on the address object. So, Address class would be modified as below.

public class Address implements Cloneable {
   private String country;
	
   private String state;
	
   private String city;

   public Object clone() throws CloneNotSupportedException {
	return super.clone();
   }

   // Getter and setter methods
}

This is the main drawback of this method that if you want to clone a nested object, then it should also implement java.lang.Cloneable and clone() method.
Many times it is not possible to modify the nested object because it is present in some external library and you do not have access to its source code.
In such case, you should not use clone() method for nested object and create a new java object for it and populate its different values yourself.
Method 2: Using copy constructor
A constructor which takes the object of its class as an argument and returns a new object with all its field values initialized to the field values of argument object is called copy constructor.
A copy constructor is used to create a copy of an object and hence its name. With copy constructors in place, you do not need java cloning mechanism as the constructor is creating a copy.
This also means that your class is not required to implement java.lang.Cloneable interface and clone() method. Example,

public class Student {
	
   private String name;

   private int age;

   /*
    * Copy constructor
    */	
   public Student(Student student) {
      // create a new student object
      Student copy = new Student();
      copy.setName(student.name);
      copy.setAge(student.age); 
   }

    // Getter and setter methods
}

Let’s test this.

public class CopyConstructorDemo {
   public static void main(String[] args) {
      // create a student object
      Student student = new Student();
      student.setAge(15);
      student.setName("John");
      // create a copy object
      Student copy = new Student(student);
      // check if they are equal
      System.out.println("Are student objects same? "
                     + student.equals(copy));
   }
}

Output is

Are student objects same? false

which shows that a copy of the student object has been created.
Method 3: Using Serialization
Serialization is considered as a technique to save the state of objects for later use but it is also a very good method for performing object cloning and that too deep cloning. Example,

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class SerializationDemo {
   public static void main(String[] args) {
      try {
         // create a student object
         Student student = new Student();
         student.setAge(15);
         student.setName("John");
         // create an output stream
         ObjectOutputStream out = new ObjectOutputStream(
                      new FileOutputStream(new File("object.ser")));
         // write object
         out.writeObject(ob);
         out.flush();
         out.close();
         // create input stream
         ObjectInputStream in = new ObjectInputStream(
                      new FileInputStream(new File("object.ser")));
         // read the object back
         Student copy = (Student)in.readObject();
         in.close();
         // check if they are equal
         System.out.println("Are student objects same? "
                     + student.equals(copy));
      } catch (FileNotFoundException e) {
         e.printStackTrace();
      } catch (IOException e) {
	 e.printStackTrace();
      } catch (ClassNotFoundException e) {
	 e.printStackTrace();
      }
   }
}

Output is

Are student objects same? false

which shows that both the objects are different and a new copy of student is created.
For serialization to work, the class whose objects are serialized should implement java.io.Serializable interface. Again. if there are nested objects as fields, then their classes should also implement this interface.
If any of the classes in do not implement it, then a java.io.NotSerializableException exception is thrown.
This method should not be preferred over other methods explained here since serialization is a very expensive operation in terms of performance.
Method 4: Using SerializationUtils
Apache Commons Util library has a class org.apache.commons.lang.SerializationUtils which provides clone method. This method accepts an object as argument and returns a copy of this object performed using serialization. Thus, with this library, you do not need to write all the serialization code shown above.
clone method also performs deep cloning.
Example,

import org.apache.commons.lang.SerializationUtils;

public class SerializationDemo {
   public static void main(String[] args) {
      // create a student object
      Student student = new Student();
      student.setAge(15);
      student.setName("John");
      // create a copy
      Student copy = (Student)SerializationUtils.clone(student);  
   }
}

For this method to work, all the classes that are involved in the copy process should implement java.io.Serializable interface.
Add following dependency for this library in your project as per the build tool being used.

// Gradle
compile group: ‘org.apache.commons’, name: ‘commons-lang3’, version: ‘3.9’

<!– Maven –>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.9</version>
</dependency>

Quick recap

  1. A class whose objects need to be cloned should implement java.lang.Cloneable interface and clone() method from java.lang.Object.
  2. If any of the above are not done, then a java.lang.CloneNotSupportedException is thrown.
  3. When calling clone() method, constructor of the class is not called.
  4. If the class of cloned object contains nested mutable object fields(or instance variables), then additional steps need to be taken for deep cloning. It may be creating copies of nested objects manually, using serialization or copy constructors.
  5. If nested fields of the class of cloned object are immutable, then you do not need to take any special measures for cloning, only default clone() method will serve the purpose.
  6. You can always check if an object implements java.lang.Cloneable by using instanceof operator as if(student instanceof Cloneable) { }
  7. Serialization can also be used for object cloning.
  8. When using serialization, all the involved classes should implement java.io.Serializable interface, else a java.io.NotSerializableException will be thrown.
  9. Methods defined in this article will work on all versions of java. Be it java 8 or versions above and below it.

Hope this post proved to be useful in explaining the concept of cloning in java. Click the clap below if you liked it.

Leave a Reply