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.
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:
- Make the class(whose objects need to be cloned) implement
java.lang.Cloneable
interface. - Override
clone
method ofjava.lang.Object
class. In the overridden method, you can write custom cloning functionality or callsuper.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.
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 ajava.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.
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
- A class whose objects need to be cloned should implement
java.lang.Cloneable
interface andclone()
method fromjava.lang.Object
. - If any of the above are not done, then a
java.lang.CloneNotSupportedException
is thrown. - When calling
clone()
method, constructor of the class is not called. - 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.
- 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. - You can always check if an object implements
java.lang.Cloneable
by usinginstanceof
operator asif(student instanceof Cloneable) { }
- Serialization can also be used for object cloning.
- When using serialization, all the involved classes should implement
java.io.Serializable
interface, else ajava.io.NotSerializableException
will be thrown. - 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.