Cloning, I believe everyone has heard of it. The world's first cloned sheep, Dolly, uses nuclear transplantation technology to cultivate new individuals in mammalian adult somatic cells, which is very magical. In fact, there is also the concept of cloning in Java, that is, to realize the copying of objects.
This article will try to introduce some in-depth questions about cloning in Java, hoping to help you better understand cloning.
Suppose you want to copy a simple variable. Very simple:
int apples = 5; int pearls = apples;
Not just the int type, the other seven primitive data types (boolean, char, byte, short, float, double.long) are also applicable to this type of situation.
But if you copy an object, the situation is a bit complicated.
Suppose I am a beginner, I would write this:
class Student { private int number; public int getNumber() { return number; } public void setNumber(int number) { this.number = number; } } public class Test { public static void main(String args[]) { Student stu1 = new Student(); stu1.setNumber(12345); Student stu2 = stu1; System.out.println("Student 1:" + stu1.getNumber()); System.out.println("Student 2:" + stu2.getNumber()); } }result:
Student 1:12345
Student 2:12345
Here we have customized a student class, which has only one number field.
We create a new student instance and assign the value to the stu2 instance. (Student stu2 = stu1;)
Let’s look at the printing results. As a novice, I patted my chest and abdomen, but the object was copied like this.
Is this really the case?
We try to change the number field of the stu2 instance, and then print the result and see:
stu2.setNumber(54321); System.out.println("Student 1:" + stu1.getNumber()); System.out.println("Student 2:" + stu2.getNumber());result:
Student 1:54321
Student 2:54321
This is strange. Why did the student number of Student 2 change and the student number of Student 1 also change?
The reason lies in the sentence (stu2 = stu1). The purpose of this statement is to assign the reference of stu1 to stu2.
In this way, stu1 and stu2 point to the same object in the memory heap. As shown in the picture:
So, how can we achieve copying an object?
Do you remember the King of All Ages Object? It has 11 methods, and there are two protected methods, one of which is the clone method.
In Java, all classes are inherited from the Object class in the Java language package by default. Check its source code. You can copy src.zip in your JDK directory to another place and decompress it, and all the source code is included. I found that there is a method with the access qualifier protected clone():
/*Creates and returns a copy of this object. The precision 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 true2) 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;
If you look closely, it is still a native method. Everyone knows that native methods are code implemented in non-Java languages and are for call by Java programs. Because Java programs run on JVM virtual machines, there is no way to access the underlying operating system-related ones, and they can only be implemented by languages close to the operating system.
The first declaration ensures that the cloned object will have a separate memory address allocation.
The second declaration shows that the original and cloned objects should have the same class type, but it is not mandatory.
The third statement shows that the original and cloned objects should be equally used by the equals() method, but it is not mandatory.
Because the parent class of each class is an Object, they all contain a clone() method, but because the method is protected, none of them can be accessed outside the class.
To copy an object, you need to override the clone method.
Why clone?
Let’s think about a question first, why do you need to clone objects? Isn't it okay to just new an object?
The answer is: the cloned object may contain some modified properties, and the properties of the new object are still the values at the time of initialization, so when a new object is needed to save the "state" of the current object, the clone method depends on the clone method. So, isn't it okay for me to assign the temporary properties of this object to my new object one by one? It's OK, but let's not talk about it first. Secondly, through the above source code, everyone has discovered that clone is a native method, which is fast and implemented at the bottom.
Let me remind you that our common Object a=new Object();Object b;b=a; This form of code copies references, that is, the address of the object in memory, and the a and b objects still point to the same object.
The object assigned through the clone method exists independently of the original object.
How to implement cloning
Let me first introduce two different cloning methods, shallowClone and deep cloning.
In Java language, data types are divided into value types (basic data types) and reference types. Value types include simple data types such as int, double, byte, boolean, char, etc., and reference types include complex types such as classes, interfaces, arrays, etc. The main difference between shallow cloning and deep cloning is whether it supports copying of member variables of reference types. The two will be introduced in detail below.
The general steps are (shallow cloning):
1. The copied class needs to implement the Clonenable interface (if you do not implement it, a CloneNotSupportedException will be thrown when calling the clone method). This interface is a tag interface (without any method)
2. Override the clone() method and set the access modifier to public. Call the super.clone() method in the method to get the required copy object. (native is a local method)
The following is a modification of the above method:
class Student implements Cloneable{private int number;public int getNumber() {return number;}public void setNumber(int number) {this.number = number;}@Override public Object clone() {Student stu = null;try{stu = (Student)super.clone();}catch(CloneNotSupportedException e) {e.printStackTrace();}return stu;}}public class Test {public static void main(String args[]) {Student stu1 = new Student();stu1.setNumber(12345);Student stu2 = (Student)stu1.clone();System.out.println("Student1:" + stu1.getNumber());System.out.println("Student2:" + stu2.getNumber());stu2.setNumber(54321);System.out.println("Student1:" + stu1.getNumber());System.out.println("Student2:" + stu2.getNumber());}}result:
Student 1:12345
Student 2:12345
Student 1:12345
Student 2:54321
If you don't believe that these two objects are not the same object, then you can take a look at this sentence:
System.out.println(stu1 == stu2); // false
The above copy is called a shallow cloning.
There is also a slightly more complex deep copy:
Let's add an Address class to the student class.
class Address {private String add;public String getAdd() {return add;}public void setAdd(String add) {this.add = add;}}class Student implements Cloneable{private int number;private Address addr;public Address getAddr() {return addr;}public void setAddr(Address addr) {this.addr = addr;}public int getNumber() {return number;}public void setNumber(int number) {this.number = number;}@Override public Object clone() {Student stu = null;try{stu = (Student)super.clone();}catch(CloneNotSupportedException e) {e.printStackTrace();}return stu;}}public class Test {public static void main(String args[]) {Address addr = new Address();addr.setAdd("Hangzhou City");Student stu1 = new Student();stu1.setNumber(123);stu1.setAddr(addr);Student stu2 = (Student)stu1.clone();System.out.println("Student 1:" + stu1.getNumber() + ",Add:" + stu1.getAddr().getAdd());System.out.println("Student 2:" + stu2.getNumber() + ",Add:" + stu2.getAddr().getAdd());}}result:
Student 1:123, Address: Hangzhou Student 2:123, Address: Hangzhou
At first glance, there is no problem, is this really the case?
We try to change the address of the addr instance in the main method.
addr.setAdd("Xihu District"); System.out.println("Student 1:" + stu1.getNumber() + ",Add:" + stu1.getAddr().getAdd()); System.out.println("Student 2:" + stu2.getNumber() + ",Add:" + stu2.getAddr().getAdd());result:
Student 1:123, Address: Hangzhou Student 2:123, Address: Hangzhou Student 1:123, Address: Xihu District Student 2:123, Address: Xihu District
This is strange, why did the addresses of both students change?
The reason is that shallow copying only copies the reference of the addr variable, and does not really open up another piece of space. After copying the value, return the reference to the new object.
So, in order to achieve true copying objects, not purely reference copying. We need to copy the Address class and modify the clone method. The complete code is as follows:
package abc; class Address implements Cloneable { private String add; public String getAdd() { return add; } public void setAdd(String add) { this.add = add; } @Override public Object clone() { Address addr = null; try{ addr = (Address)super.clone(); } catch(CloneNotSupportedException e) { e.printStackTrace(); } return addr; } } class Student implements Cloneable{ private int number; private Address addr; public Address getAddr() { return addr; } public void setAddr(Address addr) { this.addr = addr; } public int getNumber() { return number; } public void setNumber(int number) { this.number = number; } @Override public Object clone() { Student stu = null; try{ stu = (Student)super.clone(); //Shallow copy}catch(CloneNotSupportedException e) { e.printStackTrace(); } stu.addr = (Address)addr.clone(); //Deep copy return stu; } } public class Test { public static void main(String args[]) { Address addr = new Address(); addr.setAdd("Hangzhou City"); Student stu1 = new Student(); stu1.setNumber(123); stu1.setAddr(addr); Student stu2 = (Student)stu1.clone(); System.out.println("Student 1:" + stu1.getNumber() + ",Address:" + stu1.getAddr().getAdd()); System.out.println("Student 2:" + stu2.getNumber() + ",Address:" + stu2.getAddr().getAdd()); addr.setAdd("Xihu District"); System.out.println("Student 1:" + stu1.getNumber() + ",Address:" + stu1.getAddr().getAdd()); addr.setAdd()); System.out.println("Student 2:" + stu2.getNumber() + ",Address:" + stu2.getAddr().getAdd()); } }result:
Student 1:123, Address: Hangzhou Student 2:123, Address: Hangzhou Student 1:123, Address: Xihu District Student 2:123, Address: Hangzhou City
This result is in line with our ideas.
Finally, we can take a look at one of the classes in the API that implements the clone method:
java.util.Date:
/** * Return a copy of this object. */ public Object clone() { Date d = null; try { d = (Date)super.clone(); if (cdate != null) { d.cdate = (BaseCalendar.Date) cdate.clone(); } } catch (CloneNotSupportedException e) {} // Won't happen return d; }This category is actually a deep copy.
Shallow clones and deep clones
1. shallow cloning
In shallow cloning, if the member variable of the prototype object is of a value type, one copy will be copied to the cloned object; if the member variable of the prototype object is of a reference type, the address of the reference object will be copied to the cloned object, that is, the member variable of the prototype object and the cloned object point to the same memory address.
Simply put, in shallow cloning, when the object is copied, only the member variable of the value type of the object is copied and the member object of the reference type is not copied.
In Java language, shallow cloning can be implemented by overwriting the clone() method of the Object class.
2. Deep cloning
In deep cloning, no matter whether the member variable of the prototype object is a value type or a reference type, one copy will be copied to the cloned object. Deep cloning also copies all referenced objects of the prototype object to the cloned object.
Simply put, in deep cloning, except for the object itself being copied, all member variables contained in the object will also be copied.
In Java language, if you need to implement deep cloning, it can be implemented by overwriting the clone() method of the Object class, or it can be implemented by serialization, etc.
(If the reference type contains many reference types, or the inner reference type class contains reference types, it will be very troublesome to use the clone method. At this time, we can use serialization to implement deep cloning of the object.)
Serialization is the process of writing objects to a stream. The object written to the stream is a copy of the original object, and the original object still exists in memory. The copy implemented by serialization can not only copy the object itself, but also copy the member objects it references. Therefore, by serializing the object to a stream and then reading it out of the stream, deep cloning can be achieved. It should be noted that the class of an object that can implement serialization must implement the Serializable interface, otherwise the serialization operation cannot be implemented.
Extended
The code of the Cloneable interface and Serializable interface provided by the Java language is very simple. They are both empty interfaces. This empty interface is also called an identification interface. There is no definition of any method in the identification interface. Its function is to tell JRE whether the implementation classes of these interfaces have a certain function, such as whether they support cloning, whether they support serialization, etc.
Solve multi-layer cloning problems
If the reference type contains many reference types, or the inner reference type class contains reference types, it will be very troublesome to use the clone method. At this time, we can use serialization to implement deep cloning of the object.
public class Outer implements Serializable{ private static final long serialVersionUID = 369285298572941L; //It is best to explicitly declare the ID public Inner inner; //Discription:[Deep copy method, requires the object and all object properties to be serialized] public Outer myclone() { Outer outer = null; try { // Serialize the object into a stream, because what is written in the stream is a copy of the object, and the original object still exists in the JVM. Therefore, using this feature, you can achieve a deep copy of the object ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(this); // Serialize the stream into an object ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bais); outer = (Outer) ois.readObject(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } return outer; }}Inner must also implement Serializable, otherwise it cannot be serialized:
public class Inner implements Serializable{ private static final long serialVersionUID = 872390113109L; //It is best to explicitly declare the ID public String name = ""; public Inner(String name) { this.name = name; } @Override public String toString() { return "The name value of Inner is: " + name; }}This can also enable the two objects to exist completely independently in the memory space without affecting each other's values.
Summarize
There are two ways to implement object cloning:
1). Implement the Cloneable interface and override the clone() method in the Object class;
2). Implement the Serializable interface and implement cloning through object serialization and deserialization, which can realize true deep cloning.
Note: cloning based on serialization and deserialization is not just a deep cloning, but more importantly, through generic limitation, it can be checked whether the object to be cloned supports serialization. This check is done by the compiler and does not throw exceptions at runtime. This solution is obviously better than cloning objects using the clone method of the Object class. It is always better to leave the problem to runtime by exposing it when compiling.
The above is all the detailed explanation of Java programming implementation object cloning (copy) code, I hope it will be helpful to everyone. Interested friends can continue to refer to other related topics on this site. If there are any shortcomings, please leave a message to point it out.