The most successful people in any walks of lives may not be the most talented ones but are definately the most passionate & dedicated ones.

Monday, August 23, 2010

Java Cloning explained with an example

What is cloning in java?
Cloning refers to creating duplicate copies of objects in java.

What are different flavours of cloning available in java?
We can implement cloning in java in two ways viz. shallow cloning and deep cloning.

Way to achieve this in java
1. Cloning can be achieved in java via the Cloneable interface.
2. Any class which is to be cloned has to implement Cloneable interface.
3. Cloneable interface is a marker interface meaning that it doesn't have methods

Role of the clone() method
1. Member of Object class
2. Method Signature -- protected Object clone() throws CloneNotSupportedException
3. Its a method used to create a copy of an object of a class which implements Cloneable interface.

Know/revise this before you proceed...
(1) The java.lang.Object class defines clone() method that returns a copy of the object (remember to type-cast the object)
(2) Assuming the subclass (which is to be cloned) implements the java.lang.Cloneable interface.
(3) The default behavior of clone() is to return a shallow copy of the object.
(4) Java classes are free to override clone() method to do more complex kinds of cloning.
(5) Cloning of objects can be very useful if you use the "prototype" design pattern or if you want to store an internal copy of an object inside an aggregation class


Concept of shallow cloning:-
(1) In a shalow copy, a new instance of the type is created and the values are copied into the new instance. The reference pointers are copied just like values and hence the reference are pointing to the original objects itself.
(2) If you go ahead and change values in either the original or cloned object's object reference gets reflected at both the places i.e. say for example if you have a class named as "classA" which have (say) three members viz. an int , a float and another reference to a "classB" and you have created an object of "classA" 'n' then cloned it and now if you change any value in "classB" then it will get reflected in the original and the cloned object.

Well if the above statement is confusing to you, then have a look at the below code snippet and all confusion would vanish:-

*******************************code snippet starts here*****************************
/*Note:-
(1) Here classA reffered in above point is "Vector"
(2) Here classB reffered in above point is "StringBuffer"
(3) Here by default, the class "Vector" which we are cloning implements "cloneable" interface so we don't have to implement it explicitly and have simply used the clone() method to clone the object. In case we have a user defined class instead of vector which is to be cloned, we would have been implementing the "cloneable" (a marker interface) as well.
*/

package util.sampleCloning;

import java.util.Vector;
import java.lang.StringBuffer;
import java.lang.Integer;

/**
* @author Vinay K Mudgil
*
*/
public class CloningExample2 {

public static void main(String[] args) {

// make a vector
Vector original = new Vector();

StringBuffer quotation = new StringBuffer("Vinay");
Integer i = new Integer(10);
String str = "abc";

// now add the stringbuffer, Integer and the String valiable to the
// vector created above.
original.add(quotation);

// now clone the Vector object
Vector clone = (Vector) original.clone();

System.out.println("printing \"original\" vector object");
printVectorContents(original);
System.out.print("\n");

System.out.println("printing \"cloned\" vector object");
printVectorContents(clone);
System.out.println("-------------------------------------");


System.out.println("now adding Integer to the original object");
original.add(i);
System.out.println("printing \"original\" vector object");
printVectorContents(original);
System.out.print("\n");

System.out.println("printing \"cloned\" vector object");
printVectorContents(clone);
System.out.println("-------------------------------------");

System.out.println("now adding String to the original object");
original.add(str);
System.out.println("printing \"original\" vector object");
printVectorContents(original);
System.out.print("\n");

System.out.println("printing \"cloned\" vector object");
printVectorContents(clone);
System.out.println("-------------------------------------");

System.out.println("now adding String to the \"clone-ed\" object");
clone.add("Vipin");
System.out.println("printing \"original\" vector object");
printVectorContents(original);
System.out.print("\n");

System.out.println("printing \"cloned\" vector object");
printVectorContents(clone);
System.out.println("-------------------------------------");

System.out.println("SO UNTIL NOW WE ARE HAVING AN IDEA THAT THE CLONED AND THE ORIGINAL OBJECT OF VECTOR CLASS ARE TWO SEPARATE OBJECTS AND WHEN WE ARE ADDING TO ONE, THE OTHER ONE DOESN'T GETS CHANGED...");

System.out.println("-------------------------------------");

System.out.println("now I am modifying the \"text\" object which is of type StringBuffer in original object which is not a primitive but a reference of another class.");
original.add(quotation.append("Kumar Mudgil"));
System.out.println("printing \"original\" vector object");
printVectorContents(original);
System.out.print("\n");

System.out.println("printing \"cloned\" vector object");
printVectorContents(clone);
System.out.println("-------------------------------------");


}// end of main() method

public static void printVectorContents(Vector vecObj) {
for (int i = 0; i < vecObj.size(); i++) {
System.out.println("value at " + i + "-th location is -->"+ vecObj.get(i));
}//end of for-loop
}// end of printVectorContents() method

} // end of class CloningExample2



AND THE OUTPUT OF THIS CODE SNIPPET IS.......


After cloning
Contents of "original":
0 (java.lang.StringBuffer): The quick brown fox

Contents of "clone":
0 (java.lang.StringBuffer): The quick brown fox

--------------------------------------------------------

After adding an Integer to the clone
Contents of "original":
0 (java.lang.StringBuffer): The quick brown fox

Contents of "clone":
0 (java.lang.StringBuffer): The quick brown fox
1 (java.lang.Integer): 5

--------------------------------------------------------

After modifying one of original's elements
Contents of "original":
0 (java.lang.StringBuffer): The quick brown fox jumps over the lazy dog.

Contents of "clone":
0 (java.lang.StringBuffer): The quick brown fox jumps over the lazy dog.
1 (java.lang.Integer): 5


*******************************code snippet ends here*******************************





Concept of deep cloning:-
(1) cloning means creating complete duplicate copy of the original object as well as the sub-objects in the object tree.
(2) Means that if "classA" shares a "has-a" releationship with "classB" and if try to clone an object of "classA" then in that case the cloned object will have a completly new object of "classB" and not mere its reference type.
(3) Also means that if we make any changes in the cloned or original object, the other one doesn't get affected or the changes made are limited to the specific object only.
(4) To implement Deep Copy, we first need to ensure that all the member classes (at all the levels - like if the member class itself has a member of some class type then that class as well... and so on) are implementing the Cloneable interface otherwise calling the clone() method on the objects of those classes will result into CloneNotSupportedException.
-->Once all the member classes implement cloneable, we override the clone() method in all those classes (even in the classes where we have only primitive type members otherwise we would not be able to call the protected clone() method of Object class on the instances of those classes inside some other class)

Below are the example which, if you read carefully, would make deep cloning crystal clear to you.

*******************************classA.java, starts here*******************************
/**
* untility class used to explain the concept of deep cloning
*/
package util.sampleCloning.deepcloning;

/**
* @author Vinay K Mudgil
*
*/
public class classA implements Cloneable{
private int x;
private int y;

public classA(int a, int b) {
this.x = a;
this.y = b;
}

public classA() {
this.x = 100;
this.y = 101;
}


public Object clone() throws CloneNotSupportedException {
return super.clone();
}//end of overridden clone method


public int getX() {
return x;
}

public void setX(int x) {
this.x = x;
}

public int getY() {
return y;
}

public void setY(int y) {
this.y = y;
}



}//end of class

*******************************classA.java, ends here*******************************


*************************DeepCloningImpl.java, starts here*******************************
/**
* class to show sample implementations and concepts of deep cloning
*/
package util.sampleCloning.deepcloning;

/**
* @author Vinay K Mudgil
*
*/
public class DeepCloningImpl implements Cloneable{

public int var;
public classA obj;

public DeepCloningImpl() {
this.var = 200;
this.obj = new classA();
}//end of constructor

public Object clone() throws CloneNotSupportedException {
DeepCloningImpl dci = (DeepCloningImpl) super.clone();
dci.obj = new classA(11,22);

return dci;
}//end of clone method

public static void main(String[] args) {

DeepCloningImpl dciOriginal = new DeepCloningImpl();
DeepCloningImpl dciCloned = null;

System.out.println("Original Object...");
System.out.println("dciOriginal.var -->"+dciOriginal.var);
System.out.println("dciOriginal.obj.getX() --> "+dciOriginal.obj.getX());
System.out.println("dciOriginal.obj.getY() --> "+dciOriginal.obj.getY());


try {
//now cloning the object.
dciCloned = (DeepCloningImpl) dciOriginal.clone();
} catch (CloneNotSupportedException cnse) {
cnse.printStackTrace();
}

System.out.println("\nCloned Object...");
System.out.println("dciCloned.var -->"+dciCloned.var);
System.out.println("dciCloned.obj.getX() --> "+dciCloned.obj.getX());
System.out.println("dciCloned.obj.getY() --> "+dciCloned.obj.getY());

System.out.println("-------------------------------");

System.out.println("Now changing in primitive member of original object and see if cloned object changes...");
dciOriginal.var = 300;

System.out.println("printing Original Object...");
System.out.println("dciOriginal.var -->"+dciOriginal.var);
System.out.println("dciOriginal.obj.getX() --> "+dciOriginal.obj.getX());
System.out.println("dciOriginal.obj.getY() --> "+dciOriginal.obj.getY());

System.out.println("\nprinting Cloned Object...");
System.out.println("dciCloned.var -->"+dciCloned.var);
System.out.println("dciCloned.obj.getX() --> "+dciCloned.obj.getX());
System.out.println("dciCloned.obj.getY() --> "+dciCloned.obj.getY());

System.out.println("-------------------------------");

System.out.println("Now changing in an object which shares a \"has-a releationship\" with original object and see if cloned object changes...");
dciOriginal.obj.setX(500);
dciOriginal.obj.setY(501);

System.out.println("printing Original Object...");
System.out.println("dciOriginal.var -->"+dciOriginal.var);
System.out.println("dciOriginal.obj.getX() --> "+dciOriginal.obj.getX());
System.out.println("dciOriginal.obj.getY() --> "+dciOriginal.obj.getY());

System.out.println("\nprinting Cloned Object...");
System.out.println("dciCloned.var -->"+dciCloned.var);
System.out.println("dciCloned.obj.getX() --> "+dciCloned.obj.getX());
System.out.println("dciCloned.obj.getY() --> "+dciCloned.obj.getY());

System.out.println("-------------------------------");

System.out.println("Now making changes in cloned object and see if original object changes i.e. the other way around of what is done above...");
dciCloned.obj.setX(900);
dciCloned.obj.setY(901);

System.out.println("printing Original Object...");
System.out.println("dciOriginal.var -->"+dciOriginal.var);
System.out.println("dciOriginal.obj.getX() --> "+dciOriginal.obj.getX());
System.out.println("dciOriginal.obj.getY() --> "+dciOriginal.obj.getY());

System.out.println("\nprinting Cloned Object...");
System.out.println("dciCloned.var -->"+dciCloned.var);
System.out.println("dciCloned.obj.getX() --> "+dciCloned.obj.getX());
System.out.println("dciCloned.obj.getY() --> "+dciCloned.obj.getY());

}//end of main() method

}//end of class

*************************DeepCloningImpl.java, ends here*******************************


Happy learning and your critical analysis pertaining to this is welcome (spare me for any typo's).

Thanks !!!

No comments:

Post a Comment