preface

In Java application development, sometimes we need to get another object cloning, for cloning, we often want to get clone of the data in the object and is the same as the original object, but to any one object changes will not affect another object, namely after cloning, two completely unrelated objects. Object cloning is not a simple assignment statement. Let’s take a look at Java object cloning (copying).

1. No copy

Let’s look at the simplest way to write it:

class Person{ private String name; private int age; public Person(String name, int age){ this.name = name; this.age = age; } public void setAge(int age){ this.age = age; } public void setName(String name) { this.name = name; } @Override public String toString(){ return "name:"+name + " age:" + age; } } public class TestClone { public static void main(String[] args) throws CloneNotSupportedException{ Person per = new Person("testClone",18); Person perClone = per; perClone.setAge(19); System.out.println(" source: "+per); System.out.println(" clone object: "+perClone); }}Copy the code

For example, code that defines a Person class with name and age attributes, creates a new Person object per, and uses the sentence Person perClone = per; To try to copy, and then change the age of the cloned object to 19, the result is unsatisfactory



Since Java object variables hold references,Person perClone = per;At most, the original object is given a different name, not a copy of the role.

2. Shallow copy

The Object class provides a clone method that can be used to clone objects.



As you can see, the built-in method of Object is a native method. Native method is the underlying method, which will not be introduced here. The clone method returns a new object and assigns values one by one to the data in the object. We add the following code to the Person class:

@Override
public Person clone() throws CloneNotSupportedException{
    return (Person)super.clone();
}
Copy the code

Since the Clone method on an Object is protected, it cannot be called directly from an Object. It must be overwritten in the class to make it public so that it can be called at will. Need to throw a CloneNotSupportedException note is method, otherwise will be at fault as follows:



Also, classes that implement the copy function must also implement the Cloneable interface, otherwise the same error will be reported. This interface is defined as follows:

public interface Cloneable {
}
Copy the code

As you can see, there’s nothing in the interface, it’s a tag interface, but you can’t use the copy function without implementing it. Let’s change the master method to the following:

public static void main(String[] args) throws CloneNotSupportedException{ Person per = new Person("testClone",18); Person perClone = per.clone(); perClone.setAge(19); perClone.setName("clone"); System.out.println(" source: "+per); System.out.println(" clone object: "+perClone); }Copy the code



Yi? At this point, isn’t that what we want? The change to the clone does not affect the original object. Is that enough?

First, since age is a basic data type, cloning with assignment is fine. Second, the String class is immutable. Changes to the String end up making a String object variable point to another String. A String object stores a reference to a String, and a shallow copy of the object makes the name in the perClone object also point to the “testClone” String. After theperClone.setName("clone"), making the name in the perClone object point to the “Clone” string, so this approach seems to achieve its goal. We can add another property to the Person class, pet, which is an object of the PET class, which is a mutable class.

class Pet{ String type; public Pet(String type) { this.type = type; } public void setType(String type) { this.type = type; } @Override public String toString(){ return this.type; }}Copy the code

Define a Pet class and add the property private Pet Pet to the Person class with the corresponding set and get methods. The main function is modified as follows:

Person per = new Person("testClone",18,new Pet("cat")); Person perClone = per.clone(); perClone.setAge(19); perClone.setName("clone"); perClone.getPet().setType("dog"); System.out.println(" source: "+per); System.out.println(" clone object: "+perClone);Copy the code



We can see that the changes to the cloned object affect the original object, indicating whether the shallow copy will not work, or whether the original object and the cloned object cannot be completely unrelated…



By simply drawing the relationship between the properties of the two objects, it is easy to understand why a change in PET by one object can affect the other. As you can see, using clone in the Object class is sometimes not enough…

3. Deep copy

Since PET is a mutable object, it also needs to be copied, rather than simply assigned, to make changes to the PET class code:

class Pet implements Cloneable{ String type; public Pet(String type) { this.type = type; } public void setType(String type) { this.type = type; } @Override public String toString(){ return this.type; } @Override public Pet clone() throws CloneNotSupportedException{ return (Pet)super.clone(); }}Copy the code

Now that the PET class has the ability to clone, modify the clone method in the Person class to look like this:

@Override
public Person clone() throws CloneNotSupportedException{
    Person cloned = (Person)super.clone();
    cloned.pet = pet.clone();
    return cloned;
}
Copy the code

Run the program again and the result is as follows:



The clone is now complete, so that modifications to the original object and the cloned object do not affect each other. At this time, the property relation diagram is:



conclusion

1. When cloning an Object, first check whether the Clone method of the Object class is available. If the properties of the Object are of basic data types or immutable objects such as String, then you can directly call the Clone method of the Object class

2. If an Object has mutable class properties, override the Clone method of the Object class

3. Want to call clone method, must implement Cloneable interface, and throw CloneNotSupportedException