Java deep clone example

How to deep clone an object in Java

In this article will present how to deep clone an object. This is required when the variables from the object are other objects or lists. What actually happens if the deep cloning technique is not applied? The child object that is not cloned will actually be the same in both parent objects.

To see how to clone a simple (shallow) object check How to clone an object in Java tutorial.

Example

In order to see how deep clone needs to be implemented will create 2 classes: User and Address. The User class will have an Adress object. Both classes need to implements Cloneable Interface.

The import part is happening in the clone method where each object needs to be cloned separately and added to the cloned parent object.

package com.admfactory.clone; public class User implements Cloneable < private String name; private String email; private int age; private Address address; public String getName() < return name; >public void setName(String name) < this.name = name; >public String getEmail() < return email; >public void setEmail(String email) < this.email = email; >public int getAge() < return age; >public void setAge(int age) < this.age = age; >public Address getAddress() < return address; >public void setAddress(Address address) < this.address = address; >@Override public Object clone() throws CloneNotSupportedException < User user; try < user = (User) super.clone(); if (address != null) user.address = (Address) address.clone(); >catch (CloneNotSupportedException e) < // this should never happen System.out.println("CloneNotSupportedException thrown " + e); return null; >return user; > @Override public String toString() < String result = "name: " + name + ", email: " + email + ", age: " + age; if (address != null) result += ", address: " + address; return result; >> 

Address.java

package com.admfactory.clone; public class Address implements Cloneable < private String street; private String city; private int number; public String getStreet() < return street; >public void setStreet(String street) < this.street = street; >public String getCity() < return city; >public void setCity(String city) < this.city = city; >public int getNumber() < return number; >public void setNumber(int number) < this.number = number; >@Override public Object clone() throws CloneNotSupportedException < try < return super.clone(); >catch (CloneNotSupportedException e) < System.out.println("CloneNotSupportedException thrown " + e); throw new CloneNotSupportedException(); >> @Override public String toString() < return number + " " + street + ", " + city; >> 

Usage

package com.admfactory.clone; public class DeepCloneExample < public static void main(String[] args) throws CloneNotSupportedException < System.out.println("Object deep cloning example"); System.out.println(); /** defining one object */ User user1 = new User(); user1.setName("John Doe"); user1.setEmail("john@example.com"); user1.setAge(100); Address address1 = new Address(); address1.setCity("London"); address1.setNumber(91); address1.setStreet("Lambard"); user1.setAddress(address1); System.out.println("User 1: "); System.out.println(user1.toString()); System.out.println(); /** * cloning the object - Notice the cast as the clone method from Cloneable * returns Object */ User user2 = (User) user1.clone(); System.out.println("User 2: "); System.out.println(user2.toString()); System.out.println(); /** changing the address for user 1 */ Address address2 = user1.getAddress(); address2.setCity("Glasgow"); address2.setNumber(12); address2.setStreet("Peter"); user1.setAddress(address2); System.out.println("User 1: "); System.out.println(user1.toString()); System.out.println(); System.out.println("User 2: "); System.out.println(user2.toString()); System.out.println(); >> 

Output

Object deep cloning example User 1: name: John Doe, email: john@example.com, age: 100, address: 91 Lambard, London User 2: name: John Doe, email: john@example.com, age: 100, address: 91 Lambard, London User 1: name: John Doe, email: john@example.com, age: 100, address: 12 Peter, Glasgow User 2: name: John Doe, email: john@example.com, age: 100, address: 91 Lambard, London 

You can notice that changing the address object from the user1 object is not affecting the address object from the user2 object. In order to see the difference, you can comment the line 46-47 from User.java and run again the test application. You can see from the output that changing the address object from user1 is affecting the address object from user2, actually is the same object assigned to both user objects.

Читайте также:  Gthtdjl d java bp string d int

References

Источник

Java Cloning – Deep and Shallow Copy – Copy Constructors

In Java, cloning is the process of creating an exact copy of the original object. It essentially means the ability to create an object with a similar state as the original object.

The Object’s clone() method provides the cloning functionality in Java.

1. What is Cloning in Java?

In simple words, cloning is about creating a copy of the original object. Its dictionary meaning is: “make an identical copy of”.

By default, Java cloning is ‘field by field copy’ because the Object class does not have any idea about the structure of the class on which the clone() method will be invoked.

So, JVM when called for cloning, does the following things:

  • If the class has only primitive data type members then a completely new copy of the object will be created and the reference to the new object copy will be returned.
  • If the class contains members of any class type then only the object references to those members are copied and hence the member references in both the original object as well as the cloned object refer to the same object.

Apart from the above default behavior, we can always override this behavior and specify your own. This is done by overriding the clone() method. Let’s see how it is done.

2. Cloneable Interface and clone() Method

In java, if a class needs to support cloning, we must do the following things:

  1. We must implement Cloneable interface.
  2. We must override clone() method from Object class.(It is weird. clone() method should have been in Cloneable interface.)

Java docs about clone() method are given below (formatted and extracted).

/** Creates and returns a copy of this object. The precise meaning of "copy" may depend on the class of the object. The general intent is that, for any object x, the expression: 1) x.clone() != x will be true 2) x.clone().getClass() == x.getClass() will be true, but these are not absolute requirements. 3) x.clone().equals(x) will be true, this is not an absolute requirement. */ protected native Object clone() throws CloneNotSupportedException;
  1. First statement guarantees that cloned object will have separate memory address assignment.
  2. Second statement suggests that original and cloned objects should have same class type, but it is not mandatory.
  3. Third statement suggests that original and cloned objects should have be equal using equals() method, but it is not mandatory.

Let’s understand the Java cloning process with an example. The Employee class has 3 fields – id , name and department .

public class Employee implements Cloneable < private int empoyeeId; private String employeeName; private Department department; public Employee(int id, String name, Department dept) < this.empoyeeId = id; this.employeeName = name; this.department = dept; >@Override protected Object clone() throws CloneNotSupportedException < return super.clone(); >//Getters and Setters >

Department class has two attributes – id and name .

public class Department < private int id; private String name; public Department(int id, String name) < this.id = id; this.name = name; >//Getters and Setters >

So, if we need to clone the Employee class, then we need to do something like this.

Great, we successfully cloned the Employee object. But, remember we have two references to the same object and now both will change the state of the object in different parts of the application. Want to see how? Let’s see.

Oops, cloned object changes are visible in the original also. This way cloned objects can make havoc in the system if allowed to do so. Anybody can come and clone your application objects and do whatever he likes. Can we prevent this??

The answer is yes, we can. We can prevent this by creating deep copying or using copy constructors. We will learn about them later in this post.

3. Shallow Copy of an Object

Shallow cloning is the “default implementation” in Java. In overridden clone() method, if we are not cloning all the object types (not primitives), then we are making a shallow copy.

All above examples are of shallow copy only, because we have not cloned the Department object on Employee class’s clone method. Now, I will move on to the next section where we will see the deep cloning.

Deep cloning or deep copying is the desired behavior in most cases. In the deep copy, we create a clone that is independent of the original object and making changes in the cloned object should not affect the original object.

Let’s see how deep copy is created in Java.

//Modified clone() method in Employee class @Override protected Object clone() throws CloneNotSupportedException

I modified the Employee classes clone() method and added following clone method in Department class.

//Defined clone method in Department class. @Override protected Object clone() throws CloneNotSupportedException

Now testing our cloning code gives the desired result and the name of the department will not be modified in the clone object.

Here, changing the state of the cloned object does not affect the original object.

So deep cloning requires satisfaction of following rules –

  • No need to separately copy primitives.
  • All the member classes in original class should support cloning and in clone method of original class in context should call super.clone() on all member classes.
  • If any member class does not support cloning then in clone method, one must create a new instance of that member class and copy all its attributes one by one to new member class object. This new member class object will be set in cloned object.

Copy constructors are special constructors in a class that takes an argument for its own class type.

So, when you pass an instance of a class to a copy constructor, the constructor will return a new instance of the class with values copied from the argument instance. It helps us to clone objects without the Cloneable interface.

Let us see an example of the copy constructors.

5.2. Watch out for Inheritance Issues

Above class PointOne looks simple and it is until comes inheritance.

When we define a child class by extending the above class, we need to define a similar constructor there also. In child class, we need to copy child-specific attributes and pass the argument to the super class’s constructor.

public class PointTwo extends PointOne < private Integer z; public PointTwo(PointTwo point)< super(point); //Call Super class constructor here this.z = point.z; >>

So, are we fine now? NO. The problem with inheritance is that exact behavior is identified only at runtime.

So, in our case, if some class passed the instance of PointTwo in constructor of PointOne . In this case, we will get the instance of PointOne in return where we passed the instance of PointTwo as an argument.

class corejava.cloning.PointOne class corejava.cloning.PointOne

Another way of creating a copy constructor is to have static factory methods. They take the class type in the method argument and create a new instance using another constructor of the class.

Then these factory methods will copy all the state data to the new class instance just created in the previous step, and return this updated instance.

public class PointOne implements Cloneable < private Integer x; private Integer y; public PointOne(Integer x, Integer y) < this.x = x; this.y = y; >public static PointOne copyPoint(PointOne point) throws CloneNotSupportedException < if(!(point instanceof Cloneable)) < throw new CloneNotSupportedException("Invalid cloning"); >//Can do multiple other things here return new PointOne(point.x, point.y); > >

7. Deep Cloning with Serialization

Serialization is another easy way of deep cloning. In this method, we serialize the object to be cloned and de-serialize it back. Obviously, the object, that needs to be cloned, should implement Serializable interface.

Before going any further, I should caution that this technique is not to be used lightly.

  1. First of all, serialization is hugely expensive. It could easily be a hundred times more expensive than the clone() method.
  2. Second, not all objects are Serializable .
  3. Third, making a class Serializable is tricky and not all classes can be relied on to get it right.
@SuppressWarnings("unchecked") public static T clone(T t) throws Exception < //Check if T is instance of Serializeble other throw CloneNotSupportedException ByteArrayOutputStream bos = new ByteArrayOutputStream(); //Serialize it serializeToOutputStream(t, bos); byte[] bytes = bos.toByteArray(); ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes)); //Deserialize it and return the new instance return (T)ois.readObject(); >

8. Cloning with Apache Commons

In Apache commons, SerializationUtils class also has a utility function for deep cloning. If you feel interested follow their official docs.

 org.apache.commons commons-lang3 3.7 
SomeObject cloned = SerializationUtils.clone(someObject);
  • When you don’t know whether you can call the clone() method of a particular class as you are not sure if it is implemented in that class, you can check with checking if the class is instance of “ Cloneable ” interface as below.
//We can do this if(obj1 instanceof Cloneable) < obj2 = obj1.clone(); >//Don't do this. Cloneable does not have any methods obj2 = (Cloneable) obj1.clone();
  • Note that no constructor is called on the Object during cloning process. As a result, it is your responsibility, to make sure all the instance fields have been properly set.
  • Also, if you are keeping track of the number of objects in the system by counting the invocation of constructors, you got a new additional place to increment the counter.

I hope that this post has been a refresher for you and helped you gain more information about Java clone method and its correct usage. It will also help in replying to Java clone interview questions.

Источник

Оцените статью