1. Introduction
While writing programs sometimes there is a need to create a copy of the object, or we can say need to clone an object. The process of creating the clone is defined by the object itself. In this tutorial, we’ll discuss two main aspects to cloning in Java:
Cloneable
interfaceclone()
method ofObject
class
The intention of cloning an object x of type T is to create an new instance of the same type T and initialize its fields with the same content of object x. The cloned object and the original object x should be different objects and changing one object should not affect the other object.
2. Cloneable interface
The class which implements the Cloneable
interface indicates that it is possible to create a clone of the objects of the class. Object.clone()
method then can be used to create a copy of the object.
If the class does not implement Cloneable
then trying to create a clone of the object by calling clone()
method results in the exception CloneNotSupportedException
being thrown.
Cloneable
is a marker interface and does not contain any method. The main purpose of this interface is to indicate that the object is cloneable.
It is the convention that the class which implements Cloneable
should override Object.clone()
method.
Object
class itself does not implementCloneable
, so the objects ofObject
class can not be cloned.
3. clone() method of Object
The signature of clone()
method is:
protected Object clone() throws CloneNotSupportedException
As you can notice, the clone()
method of Object
is protected
. The class which override this method should make it public
.
java.lang.Object
provides default implementation of clone()
method in Java. It’s declared as native in the Object
class, so implemented in native code. By convention, you should use super.clone()
method in the overridden method so that it uses the Object.clone()
method.
This method creates and returns a copy of this object. How should a clone of the object should look like depends on the class. Generally for any given object obj
the cloning proves should meet the following conditions:
obj.clone() != obj
should betrue
.obj.clone().getClass() == obj.getClass()
should betrue
obj.clone().equals(obj)
should betrue
Note that these are not absolute conditions to meet. While cloning objects, the general intention is that the cloned object should not have a common mutable field, i.e. the change of a mutable field in cloned object should not affect the original object or vice-versa. If there is any such field, then changes may be required for that specific field(s). One such example is cloning an immutable object. In such case, copies of reference type field(s) is created.
4. Shallow copy and Deep copy
The clone()
method creates a new instance of the class of the object to be cloned and initializes all its fields with the same content as of cloned object. The contents of the fields are not themselves cloned. So we can say that the clone()
creates a “shallow” copy of the object not a “deep” copy. Let us understand this with the help of an example.
Suppose we have a class Employee
.
public class Employee implements Cloneable { int id; String name; Address address; }
When the object of this class is created by invoking clone()
method, the contents of id
, name
and address
will be copied to the cloned object as if by assignment. Since address
is a reference variable, the cloned object will have the same address
value and both objects will point to the same object in the memory. The reason is the content of the address
field are not cloned. This is an example of “shallow” copy.
By default,
clone()
creates a shallow copy.
5. Advantages of cloning
- It is a simple way to creating copy of objects.
clone()
method is a convenient way of copying arrays.- Cloning is a reusable way of creating objects. It is possible for you to define your own logic while creating a clone based on requirement.
6. Disadvantages of cloning
- Cloning an object in Java involves some code change like implementing
Cloneable
interface, overridingclone()
method. - Cloning an object is different than using a constructor. So we have no control over object creation as we have while using constructor.
7. Example of shallow copy
Let us create a class Employee
which implements Cloneable
.
public class Employee implements Cloneable { int id; String name; Address address; // constructor public Employee(int id, String name, Address address) { super(); this.id = id; this.name = name; this.address = address; } // override clone method public Object clone() throws CloneNotSupportedException { return super.clone(); } // regular getter and setter public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } @Override public String toString() { return "Employee [id=" + id + ", name=" + name + ", address=" + address + "]"; } }
The Address class has the following code:
class Address { String city; String state; public String getCity() { return city; } public void setCity(String city) { this.city = city; } public String getState() { return state; } public void setState(String state) { this.state = state; } public Address(String city, String state) { super(); this.city = city; this.state = state; } @Override public String toString() { return "Address [city=" + city + ", state=" + state + "]"; } }
Lets now test the cloning of Employee
objects.
public class Test { public static void main(final String[] arguments) throws CloneNotSupportedException { Object o; // create an Address Address address = new Address("dummy city", "dummy state"); // create an Employee Employee e1 = new Employee(1, "James", address); // clone an Employee Employee e2 = (Employee) e1.clone(); // change city in address in Employee object e1.getAddress().setCity("new dummy city"); // print Employee e1 System.out.println("Employee e1: " + e1); // changes made in e1 should also reflect in e2 as this is shallow copy System.out.println("Employee e2: " + e2); } }
Output
Employee e1: Employee [id=1, name=James, address=Address [city=dummy city, state=dummy state]] Employee e2: Employee [id=1, name=James, address=Address [city=dummy city, state=dummy state]]
You can notice that clone()
creates a shallow copy as change in address of e1 changes the address in e2.
8. Example of deep copy
Let us change our previous example of shallow copy to make it deep copy. We’ll update the clone()
method of Employee
and Address
class.
public class Employee implements Cloneable { int id; String name; Address address; // constructor public Employee(int id, String name, Address address) { super(); this.id = id; this.name = name; this.address = address; } // override clone method public Object clone() throws CloneNotSupportedException { Employee employee = null; try { employee = (Employee) super.clone(); } catch (CloneNotSupportedException e) { employee = new Employee(this.getId(), this.getName(), this.getAddress()); } employee.address = (Address) this.address.clone(); return employee; } // regular getter and setter public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } @Override public String toString() { return "Employee [id=" + id + ", name=" + name + ", address=" + address + "]"; } } class Address { String city; String state; public Address(String city, String state) { super(); this.city = city; this.state = state; } @Override public Object clone() { try { return (Address) super.clone(); } catch (CloneNotSupportedException e) { return new Address(this.getCity(), this.getState()); } } public String getCity() { return city; } public void setCity(String city) { this.city = city; } public String getState() { return state; } public void setState(String state) { this.state = state; } @Override public String toString() { return "Address [city=" + city + ", state=" + state + "]"; } }
Let us now test our code.
public class Test { public static void main(final String[] arguments) throws CloneNotSupportedException { Object o; // create an Address Address address = new Address("dummy city", "dummy state"); // create an Employee Employee e1 = new Employee(1, "James", address); // clone an Employee Employee e2 = (Employee) e1.clone(); // change city in address in Employee object e1.getAddress().setCity("new dummy city"); // print Employee e1 System.out.println("Employee e1: " + e1); // changes made in e1 should also reflect in e2 as this is shallow copy System.out.println("Employee e2: " + e2); } }
Output
Employee e1: Employee [id=1, name=James, address=Address [city=new dummy city, state=dummy state]] Employee e2: Employee [id=1, name=James, address=Address [city=dummy city, state=dummy state]]