Car c1=new Car();
Car c2=c1;
Here, in this case any changes you make to object c1 will reflect in object c2 and vice versa.That means 'c1==c2' will return true,both the reference variables c1 and c2 are referring to the same object.Well, if this is what you want, then no issues but if you dont want the change made in c2 to be seen in c1 or any change made in c1 to be seen in c2, then cloning comes into picture.
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.Let us see what is shallow cloning and what is deep cloning.
Shallow cloning
One method of copying an object is the shallow copy.Let me to explain this concept with an example:
class Fan {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.
String brand;
int price;
public Fan(String brand, int price) {
this.brand = brand;
this.price = price;
}
}
class Room {
int roomNo;
String roomName;
Fan fan;
public Room(int roomNo, String roomName, Fan fan) {
this.roomNo = roomNo;
this.roomName = roomName;
this.fan = fan;
}
}
The shallow copy is done for roomObj and new object roomObj_cloned is created but contained object(i.e.fan) of roomObj is not copied. The original object roomObj and cloned object roomObj_clone are sharing the same fan object. See the diagram given below.
If your class contains primitive attributes or immutable objects like String,Wrapper classes then shallow cloning makes a copy of primitive data types and immutable objects for the cloned object.In the above example, the roomObj contains primitive 'roomNo' field and immutable String object 'roomName'.You can see in the above diagram that shallow cloning creates a separate copy of these fields for the cloned object roomObj_cloned.Hence if the cloned object makes any change to the content of these variables, then it won't affect the original object and vice versa.
if the object being cloned contains a reference variable called obRef, then when the clone is made, obRef in the clone will refer to the same object as does obRef in the original. If the clone makes a change to the contents of the object referred to by obRef, then it will be changed for the original object, too.You can see in the above diagram that objects roomObj and roomObj_cloned are sharing the same Fan object.
Shallow Cloning Implementation
Object class provides a clone method and provides support for the shallow copy.The clone( ) method generates a duplicate copy of the object on which it is called. Only classes that implement the Cloneable interface can be cloned.The Cloneable interface defines no members. It is used to indicate that a class allows a bitwise copy of an object (that is, a clone) to be made. If you try to call clone( ) on a class that does not implement Cloneable, a CloneNotSupportedException is thrown. When a clone is made, the constructor for the object being cloned is not called. A clone is simply an exact copy of the original.
Object class clone method.
protected Object clone()
throws CloneNotSupportedException
It returns ‘Object’ as type and you need to explicitly cast back to your original object.However, overriding clone() to return the appropriate type is preferable and eliminates the need for casting in the client (using covariant return types, since J2SE 5.0).
Cloning is a potentially dangerous action, because it can cause unintended side effects. For example, if the object being cloned contains a reference variable called obRef, then when the clone is made, obRef in the clone will refer to the same object as does obRef in the original. If the clone makes a change to the contents of the object referred to by obRef, then it will be changed for the original object, too. Here is another example. If an object opens an I/O stream and is then cloned, two objects will be capable of operating on the same stream. Further, if one of these objects closes the stream, the other object might still attempt to write to it, causing an error.
Because cloning can cause problems, clone( ) is declared as protected inside Object. This means that it must either be called from within a method defined by the class that implements Cloneable, or it must be explicitly overridden by that class so that it is public.The overridden public clone method should provide its own meaning for copy or to the least it should invoke the super.clone().When you invoke the super.clone() then you are dependent on the Object class’s implementation and what you get is a shallow copy.
Shallow cloning Example
In this example we are cloning a Room object which contains primitive field roomNo, immutable String object roomName and mutable Fan object.
package clone; class ShallowCloning { /** * @param args * */ public static void main(String[] args) { Fan fan = new Fan("USHA", 2000); Room roomObj = new Room(100, "Rose", fan); Room roomObj_cloned = (Room) roomObj.clone(); 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); } } class Fan { 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 Cloneable interface. class Room implements Cloneable { int roomNo; // primitive type. String roomName; // immutable object. Fan fan; // mutable object. public Room(int roomNo, String roomName, Fan fan) { this.roomNo = roomNo; this.roomName = roomName; this.fan = fan; } /** * Room class overridding Object class clone method with public visibility. * You can override the clone method to return the Room type using covariant * return types, since J2SE 5.0 **/ public Object clone() { try { /** * Calling Object class clone method.That means performing shallow * cloning. **/ return super.clone(); } catch (CloneNotSupportedException e) { // throws when class not implements Cloneable interface. e.printStackTrace(); return null; } } // Overridden to print the object state. public String toString() { return "RoomNo=" + roomNo + " RoomName=" + roomName + " Fan " + fan; } }The output will be
Original object:roomObj=RoomNo=100 RoomName=Rose Fan Brand=USHA Price=2000
Cloned object:roomObj_cloned=RoomNo=100 RoomName=Rose Fan Brand=USHA Price=2000
After modifying the cloned object
Original object:roomObj=RoomNo=100 RoomName=Rose Fan Brand=BAJAJ Price=3000
Cloned object:roomObj_cloned=RoomNo=101 RoomName=Lotus Fan Brand=BAJAJ Price=3000
Notice that changes made to the content of fields RoonNo and RoonName of cloned object doesn't affect the original object.But changes made to the Fan object of cloned object affects the original object.
As we discussed above shallow cloning creating a separate copy of primitive attributes(here roomNo) and immutable objects(here roomName) for the cloned object.In our example both the original object and the cloned object has its own 'roomNo' and 'roomName' fields.Hence if the cloned object makes any change to the content of these variables, then it won't affect the original object and vice versa.
If the object being cloned contains a reference variable called obRef, then when the clone is made, obRef in the clone will refer to the same object as does obRef in the original.Hence in our example both the original object roomObj and the cloned object roomObj_cloned are sharing the same Fan object.if the cloned object makes any change to the Fan object , then it will affect the original object and vice versa.
Deep Cloning
In deep copy the object is copied along with the objects it refers to, which is not done by the default cloning mechanism.Let me to explain this mechanism with above mentioned Room class.The deep copy is done for roomObj and new object roomObj_cloned is created.See the diagram given below.
Can you see any difference in this diagram when comparing with the shallow cloning diagram?.Only difference is that original object roomObj and the cloned object roomObj_cloned has its own Fan object(NOTE: In the shallow cloning both of them were sharing the same Fan object).So any changes made to Fan object in roomObj_cloned will not reflect in the original object roomObj and vice versa.
Deep Cloning Implementation
We need to override the clone() method for the classes having non-primitive type members to achieve Deep Copy as Deep Copy requires the member objects to be cloned as well, which is not done by the default cloning mechanism.For Deep Copy, we need to ensure that the member classes also implement the Cloneable interface otherwise calling the clone() method on the objects of those classes will result into CloneNotSupportedException. So, 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. After that 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) and finally calling clone() method on the object members in the overriden clone() method definition.
Deep cloning Example
In this example we are cloning a Room object which contains primitive field roomNo, immutable String object roomName and mutable Fan object. In order to do deep copy of Room class, we will also have to implement Cloneable interface in Fan class and override clone method in Fan class too. If Fan class contains references of other objects, then you will have to implement Cloneable interface and override clone method in each sub class too.
package clone; class DeepCloning { /** * @param args * */ public static void main(String[] args) { Fan fan = new Fan("USHA", 2000); Room roomObj = new Room(100, "Rose", fan); Room roomObj_cloned = (Room) roomObj.clone(); 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); } } // Fan class implementing mandatory Cloneable interface. class Fan implements Cloneable { String brand; int price; public Fan(String brand, int price) { this.brand = brand; this.price = price; } public Object clone() { try { /** * Calling Object class clone method.That means performing shallow * cloning. **/ return super.clone(); } catch (CloneNotSupportedException e) { // throws when class not implements Cloneable interface. e.printStackTrace(); return null; } } public String toString() { return "Brand=" + brand + " Price=" + price; } } // Room class implementing mandatory Cloneable interface. class Room implements Cloneable { int roomNo; // primitive type. String roomName; // immutable object. Fan fan; // mutable object. public Room(int roomNo, String roomName, Fan fan) { this.roomNo = roomNo; this.roomName = roomName; this.fan = fan; } /** * Room class overridding Object class clone method with public visibility. * You can override the clone method to return the Room type using covariant * return types, since J2SE 5.0 **/ public Object clone() { try { /** * Calling Object class clone method.That means performing shallow * cloning. **/ Room clonedRoom = (Room) super.clone(); /** * Explicitly calling the clone method of the member object fan to * create a copy of it. **/ if (clonedRoom.fan != null) { clonedRoom.fan = (Fan) clonedRoom.fan.clone(); } return clonedRoom; } catch (CloneNotSupportedException e) { // throws when class not implements Cloneable interface. e.printStackTrace(); return null; } } // Overridden to print the object state. public String toString() { return "RoomNo=" + roomNo + " RoomName=" + roomName + " Fan " + fan; } }The output will be
Original object:roomObj=RoomNo=100 RoomName=Rose Fan Brand=USHA Price=2000
Cloned object:roomObj_cloned=RoomNo=100 RoomName=Rose Fan Brand=USHA Price=2000
After modifying the cloned object
Original object:roomObj=RoomNo=100 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 was different in shallow cloning).
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.
Well this approach has got its own limitations and issues:
- Not all objects are serializable.
- As you cannot serialize a transient variable, using this approach you cannot copy the transient variables.
- Next one is the performance issue.Serialization is more expensive than using object.clone().I say, there will be vast difference in the performance. If your code is performance critical, I suggest don't go for this approach. It takes almost 100 times more time to deep copy the object than the way you do by implementing Cloneable interface.
- Serialization is not simple to implement for a complex object graphs.
clone() and final fields
Generally, clone() is incompatible with final fields.It is impossible to assign a final field within a clone() method; a compiler error is the result. Where the value of the field is a primitve or immutable object this is okay; just let the Object.clone() to copy the reference and both the original and its clone will share the same object.But where the value is a mutable object it must be deep copied. The only solution is to remove the final modifier from the field, giving up the benefits the modifier conferred.For this reason, some programmers suggest to make the objects in the hierarchy Serializable, and create copies by serializing the old object and then creating a new object from the resulting bitstream, which handles final data members correctly, but is significantly slower.
Clone has its own problems
Since the clone() method is protected, subclasses have to explicitly agree to be cloneable by overriding this protected method with a public method. All of the Collections classes do this. The subclass also has to implement Cloneable for the default cloning mechanism in Object.clone() to work.
No constructor is called on the object being cloned. As a result, it is your responsibility, to make sure all the members have been properly set. Lets consider a class keeping track of the total number of objects of that type, using a static int member. Generally in the constructors you would increase the count. But, when you clone the object, since no constructor is called, the count will not get updated and wont reflect the correct number of objects.
If the class has final fields, these can't be given a value in the clone method. This leads to problems with properly initializing the object's final fields. If the final field is referring to some internal state of the object, then the cloned object ends up sharing the internal state and this surely is not correct for mutable objects.
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.
One more disadvantage with this clone system is that, most of the interface / abstract class writers in java forget to put a public clone method. For example you can take List. So when you want to clone their implementations you have to ignore the abstract type and use actual implementations like ArrayList by name. This completely removes the advantage and goodness of abstractness.
No comments:
Post a Comment