Table of Contents
Cloning means creating a copy of the original object. Its dictionary meaning is: “make an identical copy of“.
By default, Java cloning is ‘field by field copy’ because the Object class does not have any idea about the structure of the class on which the clone() method will be invoked.
So, JVM when called for cloning, does the following things:
- If the class has only primitive data type members then a completely new copy of the object will be created and the reference to the new object copy will be returned.
- If the class contains members of any class type then only the object references to those members are copied and hence the member references in both the original object as well as the cloned object refer to the same object.
Apart from the above default behavior, we can always override this behavior and specify your own. This is done by overriding the clone()
method. Let’s see how it is done.
Object cloning means to create an exact copy of the original object.
Learn Coding in your Language! Enroll Here!
If a class needs to support cloning, it must implement java.lang.Cloneable interface and override clone() method from Object class. Syntax of the clone() method is :
protected Object clone() throws CloneNotSupportedException
If the object’s class doesn’t implement Cloneable interface then it throws an exception ‘CloneNotSupportedException’ .
// Java code for cloning an object class Test implements Cloneable { int a; int b; // Parameterized constructor Test( int a, int b) { this .a = a; this .b = b; } // Method that calls clone() Test cloning() { try { return (Test) super .clone(); } catch (CloneNotSupportedException e) { System.out.println( "CloneNotSupportedException is caught" ); return this ; } } } class demo { public static void main(String args[]) { Test obj1 = new Test( 1 , 2 ); Test obj2 = obj1.cloning(); obj1.a = 3 ; obj1.b = 4 ; System.out.println( "Object2 is a clone of object1" ); System.out.println( "obj1.a = " + obj1.a + " obj1.b = " + obj1.b); System.out.println( "obj2.a = " + obj2.a + " obj2.b = " + obj2.b); } } |
Output :
Object2 is a clone of object1 obj1.a = 3 obj1.b = 4 obj2.a = 1 obj2.b = 2
Object Cloning in Java
The object cloning is a way to create exact copy of an object. The clone() method of Object class is used to clone an object.
The java.lang.Cloneable interface must be implemented by the class whose object clone we want to create. If we don’t implement Cloneable interface, clone() method generates CloneNotSupportedException.
The clone() method is defined in the Object class. Syntax of the clone() method is as follows:
- protected Object clone() throws CloneNotSupportedException
Why use clone() method ?
The clone() method saves the extra processing task for creating the exact copy of an object. If we perform it by using the new keyword, it will take a lot of processing time to be performed that is why we use object cloning.
Advantage of Object cloning
1: What is the default value of a boolean in Java?
Although Object.clone() has some design issues but it is still a popular and easy way of copying objects. Following is a list of advantages of using clone() method:
- You don’t need to write lengthy and repetitive codes. Just use an abstract class with a 4- or 5-line long clone() method.
- It is the easiest and most efficient way for copying objects, especially if we are applying it to an already developed or an old project. Just define a parent class, implement Cloneable in it, provide the definition of the clone() method and the task will be done.
- Clone() is the fastest way to copy array.
Disadvantage of Object cloning
Following is a list of some disadvantages of clone() method:
- To use the Object.clone() method, we have to change a lot of syntaxes to our code, like implementing a Cloneable interface, defining the clone() method and handling CloneNotSupportedException, and finally, calling Object.clone() etc.
- We have to implement cloneable interface while it doesn’t have any methods in it. We just have to use it to tell the JVM that we can perform clone() on our object.
- Object.clone() is protected, so we have to provide our own clone() and indirectly call Object.clone() from it.
- Object.clone() doesn’t invoke any constructor so we don’t have any control over object construction.
- If you want to write a clone method in a child class then all of its superclasses should define the clone() method in them or inherit it from another parent class. Otherwise, the super.clone() chain will fail.
- Object.clone() supports only shallow copying but we will need to override it if we need deep cloning.
Shallow Copy of an Object
Shallow cloning is the “default implementation” in Java. In overridden clone()
method, if we are not cloning all the object types (not primitives), then we are making a shallow copy.
Deep Copying in Java
Deep cloning or deep copying is the desired behavior in most cases. In the deep copy, we create a clone that is independent of the original object and making changes in the cloned object should not affect the original object.
Let’s see how deep copy is created in Java.
//Modified clone() method in Employee class
@Override
protected Object clone() throws CloneNotSupportedException {
Employee cloned = (Employee)super.clone();
cloned.setDepartment((Department)cloned.getDepartment().clone());
return cloned;
}
Here , we modified the Employee
classes clone()
method and added following clone
method in Department
class.
//Defined clone method in Department class.
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
Now testing our cloning code gives the desired result and the name of the department will not be modified in the clone object.
public class TestCloning
{
public static void main(String[] args) throws CloneNotSupportedException
{
Department hr = new Department(1, "Human Resource");
Employee original = new Employee(1, "Admin", hr);
Employee cloned = (Employee) original.clone();
//Let change the department name in cloned object and we will verify in original object
cloned.getDepartment().setName("Finance");
System.out.println(original.getDepartment().getName());
System.out.println(cloned.getDepartment().getName());
}
}
Human Resource
Finance
Here, changing the state of the cloned object does not affect the original object.
So deep cloning requires satisfaction of following rules –
- No need to separately copy primitives.
- All the member classes in original class should support cloning and in clone method of original class in context should call
super.clone()
on all member classes. - If any member class does not support cloning then in clone method, one must create a new instance of that member class and copy all its attributes one by one to new member class object. This new member class object will be set in cloned object.
Learn to code from industry experts! Enroll here
Deep Cloning with Serialization
Serialization is another easy way of deep cloning. In this method, we serialize the object to be cloned and de-serialize it back. Obviously, the object, that needs to be cloned, should implement Serializable
interface.
Before going any further, you should be aware that this technique is not to be used lightly.
- First of all, serialization is hugely expensive. It could easily be a hundred times more expensive than the
clone()
method. - Second, not all objects are
Serializable
. - Third, making a class
Serializable
is tricky and not all classes can be relied on to get it right.
Java deep copy using in-memory serialization
We know that the easiest way of deep cloning (with some performance overhead) or deep copy is Serialization. Java serialization involves serializing the object into bytes and from bytes to object again.
It is better to use in memory deep cloning whenever it is the only need and you don’t need to persist the object for future use. In this Java deep cloning example, you can see one mechanism of in-memory deep cloning for your reference.
Please remember that deep cloning is evil for singleton pattern. It makes possible of having multiple instances of singleton classes.
1. Java deep copy example
In this program, we can see a demo class named SerializableClass
. This has three variables i.e. firstName
, lastName
and permissions
. I will add a deepCopy() instance level method to this class. Whenever invoked on an instance of SerializableClass
, it will return exact clone / deep copy of that instance.
For deep cloning, we have to first serialization and then deserialization. For serialization, I have used ByteArrayOutputStream
and ObjectOutputStream
. For deserialization, I have used ByteArrayInputStream
and ObjectInputStream
.
Grab the opportunity to learn Java with Entri! Click Here
package serializationTest; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.ArrayList; import java.util.List; public class SerializableClass implements Serializable { private static final long serialVersionUID = 1L; private String firstName = null ; private String lastName = null ; @SuppressWarnings ( "serial" ) private List permissions = new ArrayList() { { add( "ADMIN" ); add( "USER" ); } }; public SerializableClass( final String fName, final String lName) { //validateNameParts(fName); //validateNameParts(lName); this .firstName = fName; this .lastName = lName; } public SerializableClass deepCopy() throws Exception { //Serialization of object ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream out = new ObjectOutputStream(bos); out.writeObject( this ); //De-serialization of object ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream in = new ObjectInputStream(bis); SerializableClass copied = (SerializableClass) in.readObject(); //Verify that object is not corrupt //validateNameParts(fName); //validateNameParts(lName); return copied; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this .firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this .lastName = lastName; } @Override public String toString() { return new StringBuilder().append(getFirstName()+ "," ) .append(getLastName()+ "," ) .append(permissions) .toString(); } } |