,

Wednesday 8 October 2014

Java clone() method (Deep copy and Shallow copy)

Clone is a function present in Object Class. To clone an object you need to implement Cloneable interface.This topic is one of the favorite topic in interviews. Clone function is protected as it cannot be called from other classes.That is why we are using doClone function inside the Employee class.

Demo 1:
package com.a.b;
class Employee {
private int id;
private String name;
public Employee(int id, String name) {
super();
this.id = id;
this.name = name;
}
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;
}
@Override
public String toString() {
return "Employee [id=" + id + ", name=" + name + "]";
}
public Object doClone() throws CloneNotSupportedException {
return this.clone();
}
}

public class MainClass {
public static void main(String[] args) {
try {
Employee object1 = new Employee(1, "Abc");
System.out.println(object1);
// cannot call object1.clone()
Employee object2 = (Employee) object1.doClone();
System.out.println(object2);
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}

The Employee class above is not implementing Cloneable interface and that is why it throws CloneNotSupportedException.

java.lang.CloneNotSupportedException: com.a.b.Employee
at java.lang.Object.clone(Native Method)
at com.a.b.Employee.doClone(MainClass.java:40)
at com.a.b.MainClass.main(MainClass.java:50)

The clone function of Object class is native. Cloneable interface is a marker interface. It is just a checkpoint for a class to implement Cloneable so that it is allowed for the object to be cloned.
We will now implement Cloneable and override clone function.

Demo 2:
package com.a.b;
class Employee implements Cloneable {
private int id;
private String name;
public Employee(int id, String name) {
super();
this.id = id;
this.name = name;
}
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;
}
@Override
public String toString() {
return "Employee [id=" + id + ", name=" + name + "]";
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}

public class MainClass {
public static void main(String[] args) {
try {
Employee object1 = new Employee(1, "Abc");
System.out.println(object1);
Employee object2 = (Employee) object1.clone();
System.out.println(object2);
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
The default implementation of clone in Employee class returns super.clone() ie clone of Object class.
The above example will create the clone of the object.
But what is shallow copy and deep copy?

Employee class implements Cloneable interface. The primitive type members are deep copied.

Demo 3:(modification in main function only)
public static void main(String[] args) {
try {
Employee object1 = new Employee(1, "Abc");
System.out.println("Original object " + object1);
Employee object2 = (Employee) object1.clone();
System.out.println("Clone object " + object2);
object1.setName("Rahool");
System.out.println("After modification of name for object1 ");
System.out.println("After modification object1 " + object1);
System.out.println("After modification object2 " + object2);

} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}

What is shallow copy?
When you have any association relationship in the class and you clone the object of containing class , it will just copy the reference and not data. We have Car associated in Employee class.

Demo 4:
package com.a.b;
class Car {
private String brand;
private String model;
public Car(String brand, String model) {
super();
this.brand = brand;
this.model = model;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
@Override
public String toString() {
return "Car [brand=" + brand + ", model=" + model + "]";
}
}

class Employee implements Cloneable {
private int id;
private String name;
public Car car;// association relationship
public Employee(int id, String name, Car car) {
super();
this.id = id;
this.name = name;
this.car = car;
}
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;
}
@Override
public String toString() {
return "Employee [carmodel=" + car.getModel() + "car brand="
+ car.getBrand() + ", id=" + id + ", name=" + name + "]";
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}

public class MainClass {
public static void main(String[] args) {
try {
Car car = new Car("Zonita", "Stromy");
Employee object1 = new Employee(1, "John", car);
System.out.println("Original object " + object1);
Employee object2 = (Employee) object1.clone();
System.out.println("Clone object " + object2);
// deep copy
object1.setName("Rahool");
System.out.println("After modification of name for object1(deep copy) ");
System.out.println("After modification object1 " + object1);// name Rahool
System.out.println("After modification object2 " + object2);// name John
                        //shallow copy
object1.car.setModel("Twinkle");
System.out.println("After modification of Car for object1(shallow copy) ");
System.out.println("After modification object1 " + object1);// car model Twinkle
System.out.println("After modification object2 " + object2);// car model Twinkle

} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}

Output:
Original object Employee [carmodel=Stromycar brand=Zonita, id=1, name=John]
Clone object Employee [carmodel=Stromycar brand=Zonita, id=1, name=John]
After modification of name for object1(deep copy)
After modification object1 Employee [carmodel=Stromycar brand=Zonita, id=1, name=Rahool]
After modification object2 Employee [carmodel=Stromycar brand=Zonita, id=1, name=John]
After modification of Car for object1(shallow copy)
After modification object1 Employee [carmodel=Twinklecar brand=Zonita, id=1, name=Rahool]
After modification object2 Employee [carmodel=Twinklecar brand=Zonita, id=1, name=John]

Even though we are changing the car model using object 1 , the modification is getting reflected for object2 as well. This is becuase of shallow copy where it is copying only the reference.
Now the question is how will you achieve deep copy even for associated members.

You can achieve this by two ways
1) Implementing Cloneable for associated class.
2) Serialization
(You can also return a new object from clone but it is not copy)

Implementing Cloneable for associated class
Implementing Cloneable interface for associated class Car will facilitate deep copy of car members also. We need to write business logic in clone function of Employe class so that we have a deep copy.

Demo 5:
package com.a.b;
class Car implements Cloneable {
private String brand;
private String model;
public Car(String brand, String model) {
super();
this.brand = brand;
this.model = model;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
@Override
protected Object clone() throws CloneNotSupportedException {
// TODO Auto-generated method stub
return super.clone();
}
@Override
public String toString() {
return "Car [brand=" + brand + ", model=" + model + "]";
}
}

class Employee implements Cloneable {
private int id;
private String name;
public Car car;
public Employee(int id, String name, Car car) {
super();
this.id = id;
this.name = name;
this.car = car;
}
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;
}
@Override
public String toString() {
return "Employee [carmodel=" + car.getModel() + "car brand="
+ car.getBrand() + ", id=" + id + ", name=" + name + "]";
}
@Override
protected Object clone() throws CloneNotSupportedException {
// deep clone of Employee members
Employee emp = (Employee) super.clone();
// deep clone of Car members
emp.car = (Car) car.clone();
return emp;
// return super.clone();
}
}

public class MainClass {
public static void main(String[] args) {
try {
Car car = new Car("Zonita", "Stromy");
Employee object1 = new Employee(1, "John", car);
System.out.println("Original object " + object1);
Employee object2 = (Employee) object1.clone();
System.out.println("Clone object " + object2);
// deep copy
object1.setName("Rahool");
System.out.println("After modification of name for object1(deep copy) ");
System.out.println("After modification object1 " + object1);// name Rahool
System.out.println("After modification object2 " + object2);// name John
                        //deep copy
object1.car.setModel("Twinkle");
System.out.println("After modification of Car for object1(associated object is also deep copied now) ");
System.out.println("After modification object1 " + object1);// car model Twinkle
System.out.println("After modification object2 " + object2);// car model Stromy
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}

Output:
Original object Employee [carmodel=Stromycar brand=Zonita, id=1, name=John]
Clone object Employee [carmodel=Stromycar brand=Zonita, id=1, name=John]
After modification of name for object1(deep copy) 
After modification object1 Employee [carmodel=Stromycar brand=Zonita, id=1, name=Rahool]
After modification object2 Employee [carmodel=Stromycar brand=Zonita, id=1, name=John]
After modification of Car for object1(associated object is also deep copied now) 
After modification object1 Employee [carmodel=Twinklecar brand=Zonita, id=1, name=Rahool]
After modification object2 Employee [carmodel=Stromycar brand=Zonita, id=1, name=John]

Serialization
You can use serialization to achieve deep copy.We have added two functions public void doSerialize(Employee emp) and public Object doDeSerialize() for the serialization process.

Demo 6:
package com.a.b.c;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
class Car implements Serializable {
private static final long serialVersionUID = 1L;
private String brand;
private String model;
public Car(String brand, String model) {
super();
this.brand = brand;
this.model = model;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
@Override
public String toString() {
return "Car [brand=" + brand + ", model=" + model + "]";
}
}

class Employee implements Cloneable, Serializable {
private int id;
private String name;
public Car car;
public Employee(int id, String name, Car car) {
super();
this.id = id;
this.name = name;
this.car = car;
}
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;
}
@Override
public String toString() {
return "Employee [carmodel=" + car.getModel() + "car brand="
+ car.getBrand() + ", id=" + id + ", name=" + name + "]";
}
public void doSerialize(Employee emp) {
ObjectOutputStream ois = null;
try {
ois = new ObjectOutputStream(new FileOutputStream("D://myFile.ser"));
ois.writeObject(emp);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public Object doDeSerialize() {
Object obj = null;
ObjectInputStream ois = null;
try {
ois = new ObjectInputStream(new FileInputStream("D://myFile.ser"));
obj = ois.readObject();

} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return obj;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return doDeSerialize();
}
}

public class MainClass {
public static void main(String[] args) {
try {
Car car = new Car("Zonita", "Stromy");
Employee object1 = new Employee(1, "John", car);
object1.doSerialize(object1);
System.out.println("Original object " + object1);
Employee object2 = (Employee) object1.clone();
System.out.println("Clone object " + object2);
// deep copy
object1.setName("Rahool");
System.out.println("After modification of name for object1(deep copy) ");
System.out.println("After modification object1 " + object1);// name Rahool
System.out.println("After modification object2 " + object2);// name John
//deep copy
object1.car.setModel("Twinkle");
System.out.println("After modification of Car for object1(deep copy) ");
System.out.println("After modification object1 " + object1);// car model Twinkle
System.out.println("After modification object2 " + object2);// car model Stromy
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}

public class MainClass {
public static void main(String[] args) {
try {
Car car = new Car("Zonita", "Stromy");
Employee object1 = new Employee(1, "John", car);
object1.doSerialize(object1);
System.out.println("Original object " + object1);
Employee object2 = (Employee) object1.clone();
System.out.println("Clone object " + object2);
// deep copy
object1.setName("Rahool");
System.out.println("After modification of name for object1(deep copy) ");
System.out.println("After modification object1 " + object1);// name Rahool
System.out.println("After modification object2 " + object2);// name John
//deep copy
object1.car.setModel("Twinkle");
System.out.println("After modification of Car for object1(deep copy) ");
System.out.println("After modification object1 " + object1);// car model Twinkle
System.out.println("After modification object2 " + object2);// car model Stromy
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}

Output:
Original object Employee [carmodel=Stromycar brand=Zonita, id=1, name=John]
Clone object Employee [carmodel=Stromycar brand=Zonita, id=1, name=John]
After modification of name for object1(deep copy) 
After modification object1 Employee [carmodel=Stromycar brand=Zonita, id=1, name=Rahool]
After modification object2 Employee [carmodel=Stromycar brand=Zonita, id=1, name=John]
After modification of Car for object1(deep copy) 
After modification object1 Employee [carmodel=Twinklecar brand=Zonita, id=1, name=Rahool]
After modification object2 Employee [carmodel=Stromycar brand=Zonita, id=1, name=John]

Conclusion : The class implementing Cloneable interface has its primitive type deep copied and associated type shallow copied and in order to make everything deep copy you can go for either cloning the associated class(Car) or serialization of the containing class(Employee).

No comments:

Post a Comment