In Java, whenever we assign an object
reference to another, it is just a bit by bit copy of the memory address, so
any changes made using first object reference will be reflected in others and
vice versa.
For example:
MyClass mc = new
MyClass();
MyClass mc_1 = mc;
In the above snippet, any changes made to using mc
object reference will be seen by mc_1, and the reverse is also true. What
if you need an object reference, which is a copy of another object, but you
want that both objects should not interfere their state, i.e. they
independently modify their instance variables, which should not be 0seen
in other object references. In short, they should have different memory
addresses, but the same state when getting copied. Afterward, they can modify
their state as needed. This is where cloning comes into the
picture.
Cloning is a mechanism that provides a way to
create a copy of an object and the term copy is specific to the implementation
of the class.
So,cloning will firmly say:
So,cloning will firmly say:
MyClass mc = new
MyClass();
i) mc.clone() !=
mc -true always
ii) mc.getClass()
== mc.clone().getClass() -true always
iii)
mc.clone().equals(mc) -true just after clone() gets called, after that,
it may be false
Significance of the above snippet:
- mc.clone() != mc means
memory address of both object reference should not be same.
- mc.getClass() ==
mc.clone().getClass() means both should be an object reference of the same
class.
- mc.clone().equals(mc) means
the moment clone() method get called both should have same state (data),
but afterwards they may have different state (data).
To implement the cloning in our class, we need to
implement the Cloneable interface. The Cloneable interface is a Marker
interface, which has no method. So, whenever we implement the Cloneable
interface, being developers, it's our responsibility to write the definition of
the clone() method.
So, what if our class is implementing the Cloneable
interface but not providing the definition of the clone() method, so nothing
will happen — cloning will not be achieved. The purpose of the Cloneable
interface is to tell the JVM that this class is eligible for cloning, but
clone() will achieve the actual cloning.
If your class will not implement the Cloneable
interface, as below, and you run it, it will throw the java.lang.CloneNotSupportedException, which is a checked exception.
public class MyClone {
int x;
public static void main(String[] args) throws CloneNotSupportedException {
MyClone
c = new MyClone();
MyClone
a = (MyClone) c.clone(); // Type-cast is required
}
}
Because clone() is a part of the Object class, and
Object won't implement the Cloneable interface, when our own class will not
implement the Cloneable interface, the JVM will not know that this class is
eligible for cloning, so CloneNotSupportedException comes out.
Only two thing are possible when we say MyClone a =
(MyClone) c.clone();:
- Either it will return the
cloned object.
- Or it will throw
CloneNotSupportedException.
As it is clear that it is not mandatory to
implement clone() in your class when you want to clone the object, if you
don't, the clone() method in Object is declared protected — only subclasses and
members of the same package will be able to invoke clone() on the object. If
you want to change that, you should override it and make it public.
Checks before the clone() method call:
if(c instanceof Cloneable) {
MyClone a
= (MyClone) c.clone();
}
Note: No
constructor gets called when clone() gets called. It's our responsibility to
properly set all the member variables of that class.
Implementation
BathTub.java
public class BathTub {
private String tubName;
public BathTub(String tubName){
this.tubName = tubName;
}
//Any Getters-Setters goes here
}
BathRoom.java
public class BathRoom implements Cloneable {
private int legth;
private int width;
private int height;
private BathTub bathTub;
public BathRoom(int legth, int width, int height, BathTub bathTub){
this.legth = legth;
this.width = width;
this.height = height;
this.bathTub = bathTub;
}
public Object clone() {
try {
return (BathRoom)super.clone();
}
catch (CloneNotSupportedException e) {
System.out.println("CloneNotSupportedException
comes out : "+e.getMessage());
}
}
//Any Getters-Setters goes here
}
Main.java
public class Main {
public static void main(String[] args) {
BathTub bathTub
= new BathTub("BreezeTub");
BathRoom bathRoom_1
= new BathRoom(10,10,12, bathTub);
BathRoom bathRoom_2
= (BathRoom)bathRoom_1.clone();
}
}
Here super.clone() is getting called inside
clone(). As we know, clone() is declared in Object, so it is inherited by every
Java object. Calling super.clone() copies our superclass' fields and makes
bitwise copies of the fields. This known as shallow copy, which
means when you copy BathRoom using clone(), the
field's length, width, and height are getting copied with their
respective values, but bathTub is copied by reference —
bit by bit, the memory address is getting copied.
Any changes you make to bathTub of
the original object will be reflected in the cloned object and vice versa.
To solve this, we need deep copy. Now, we need to change the BathTub class
as well implement the "Cloneable" interface and clone() method, then
call the clone() method of the BathTub object inside the
clone() method of the BathRoom object.
New Implementation
BathTub.java
public class BathTub {
private String tubName;
public BathTub(String tubName){
this.tubName = tubName;
}
public Object clone() {
try {
return (BathTub)super.clone();
}
catch (CloneNotSupportedException e) {
System.out.println("CloneNotSupportedException
comes out : "+e.getMessage());
}
}
//Any Getters-Setters goes here
}
BathRoom.java
public class BathRoom implements Cloneable {
private int legth;
private int width;
private int height;
private BathTub bathTub;
public BathRoom(int legth, int width, int height, BathTub bathTub){
this.legth = legth;
this.width = width;
this.height = height;
this.bathTub = bathTub;
}
public Object clone() {
BathRoom
bathRoom = null;
try {
bathRoom
= (BathRoom)super.clone();
}
catch (CloneNotSupportedException e) {
System.out.println("CloneNotSupportedException
comes out : "+e.getMessage());
}
bathRoom.bathTub
= (BathTub) bathTub.clone();
return bathRoom;
}
//Any Getters-Setters goes here
}
Main.java
public class Main {
public static void main(String[] args) {
BathTub
bathTub = new BathTub("BreezeTub");
BathRoom
bathRoom_1 = new BathRoom(10,10,12, bathTub);
BathRoom
bathRoom_2 = (BathRoom)bathRoom_1.clone();
}
}
I hope that this will give a better
understanding of cloning and its implementation. Happy learning!