Monday, 9 July 2012

Deep cloning using serialization in Java


Cloning


Cloning means creating a copy of the object( not just the object reference copying as mentioned above). Cloning creates and returns a copy of the object, with the same class and with all the fields having the same values.

Car c1=new Car();
Car c2=(Car)c1.clone();

Here

  • c1!=c2 returns true,means c1 and c2 are referring to two different memory locations i.e. two different different objects.
  • c1.getClass()==c2.getClass() returns true means the cloned object and the original object should be of the same type.
  • c1.equals(c2) returns true means the cloned object data should be equal to the original one(Note: it can be changed any time after cloning).

Java supports two type of cloning: - Deep and shallow cloning.


By default shallow copy is used in Java. Object class has a method clone() which does shallow cloning.Shallow clone only copies the top level structure of the object not the lower levels.It is an exact bit copy of all the attributes.If the original object contains an object reference field, shallow cloning won't copy the object referred by that reference variable instead it just copy the reference i.e. the memory address.Hence both the original and cloned objects will share the same member object.

We need to override the Object.clone() method with extra logic to support the cloning of the member objects.This mechanism is called deep cloning.Please visit Cloning of object in Java - Shallow Copy and Deep Copy to learn more about deep cloning.Deep cloning has its own limitations.

Deep cloning issues


When you need a deep copy then you need to implement it yourself. When the copied object contains some other object its references are copied recursively in deep copy. When you implement deep copy be careful as you might fall for cyclic dependencies. If you don’t want to implement deep copy yourselves then you can go for serialization. It does implements deep copy implicitly and gracefully handling cyclic dependencies.

Another problem arises when you try deep copying of a complex object. You're assuming that the clone() method of all member object variables also does deep copy; this is too risky of an assumption. You must control the code in all classes, or you must know that all classes involved in deep copy operation do such a copy in the right way.


Alternative way for deep copy -Serialization



You can do deep copy through serialization. What does serialization do? It writes out the whole object graph into a persistant store and read it back when needed, which means you will get a copy of the whole object graph when you read it back. This is exactly what you want when you deep copy an object. Note, when you deep copy through serialization, you should make sure that all classes in the object's graph are serializable. Let me explain you this alternative way with an example. If you want to know about serialization first, check it out  Serialization in java.

Example:

In this example we are doing a deep cloning of a Room object which contains a Fan object.


package clone;



import java.io.ByteArrayInputStream;

import java.io.ByteArrayOutputStream;

import java.io.IOException;

import java.io.ObjectInputStream;

import java.io.ObjectOutputStream;

import java.io.Serializable;



class DeepCloningUsingSerialization {



 /**

  * @param args

  * */

 public static void main(String[] args) {



  // Create the Room object roomObj



  Fan fan = new Fan("USHA", 2000);

  Room roomObj = new Room(100, "Rose", fan);



  ObjectOutputStream out = null;

  ObjectInputStream in = null;



  try {

   // Serialize the roomObj.



   ByteArrayOutputStream bos = new ByteArrayOutputStream();

   out = new ObjectOutputStream(bos);

   out.writeObject(roomObj);



   // Clone(i.e.Deserialize) the roomObj.



   ByteArrayInputStream bis = new ByteArrayInputStream(

     bos.toByteArray());

   in = new ObjectInputStream(bis);

   Room roomObj_cloned = (Room) in.readObject();



   System.out.println("Original object:roomObj=" + roomObj);

   System.out

     .println("Cloned object:roomObj_cloned=" + roomObj_cloned);



   /**

    * Modifying the cloned object field values. For simplicity I am not

    * using getters and setters for retrieving and modifying values.

    **/

   roomObj_cloned.roomName = "Lotus";

   roomObj_cloned.roomNo = 101;

   roomObj_cloned.fan.brand = "BAJAJ";

   roomObj_cloned.fan.price = 3000;



   System.out.println("After modifying the cloned object");



   System.out.println("Original object:roomObj=" + roomObj);

   System.out

     .println("Cloned object:roomObj_cloned=" + roomObj_cloned);



  } catch (IOException e) {

   e.printStackTrace();

  } catch (ClassNotFoundException e) {

   e.printStackTrace();

  } finally {

   try {

    out.close();

    in.close();

   } catch (IOException e) {

    e.printStackTrace();

   }

  }



 }

}



// Fan class implementing mandatory Serializable interface.



class Fan implements Serializable {



 private static final long serialVersionUID = 244624585380310637L;

 String brand;

 int price;



 public Fan(String brand, int price) {

  this.brand = brand;

  this.price = price;

 }



 public String toString() {

  return "Brand=" + brand + " Price=" + price;

 }

}



// Room class implementing mandatory Serializable interface.



class Room implements Serializable {



 private static final long serialVersionUID = -5171291379935387717L;

 int roomNo;

 String roomName;

 Fan fan;



 public Room(int roomNo, String roomName, Fan fan) {

  // this.roomNo = roomNo;

  this.roomName = roomName;

  this.fan = fan;

 }



 // Overridden to print the object state.



 public String toString() {

  return "RoomNo=" + roomNo + " RoomName=" + roomName + " Fan " + fan;

 }

}


The output will be



Original object:roomObj=RoomNo=0 RoomName=Rose Fan Brand=USHA Price=2000
Cloned object:roomObj_cloned=RoomNo=0 RoomName=Rose Fan Brand=USHA Price=2000
After modifying the cloned object
Original object:roomObj=RoomNo=0 RoomName=Rose Fan Brand=USHA Price=2000
Cloned object:roomObj_cloned=RoomNo=101 RoomName=Lotus Fan Brand=BAJAJ Price=3000

Notice that changes made to the content of fan object of cloned object does't affect the original object( behavior will be different in shallow cloning).

No comments:

Post a Comment