1. The sequence
The clone method in Object is defined as:
protected native Object clone() throws CloneNotSupportedException;
Copy the code
Calling the Clone () method requires the Object to implement the Cloneable interface, which determines the implementation behavior of the clone method protected in Object:
- If a class implements the Cloneable interface, Object’s Clone method returns a domain-by-domain copy of the Object.
- If a class does not implement Cloneable interface, the object will throw CloneNotSupportedException abnormalities.
For the Cloneable interface, it changes the behavior of protected methods in the superclass.
2. Clone method specification
If the Cloneable interface is implemented to act on a class, the class and all its superclasses must comply with a fairly complex, unenforceable, and largely undocumented protocol. The result is an out-of-language mechanism for creating objects without calling the constructor.
The generic conventions for the Clone method are very weak, and the following conventions are taken from the Object specification
The Clone method is used to create and return a copy of an object.
For any object x, the expression x.lone ()! =x will be true, and the expression x.lone ().getClass() == x.gottclass () will be true, but this is not an absolute requirement.
2. In general, the expression x.lon.equals (x) will be true, which, like 1, is not an absolute requirement.
3. Problems with conventions
Copying an object often results in the creation of a new instance of its class, but it also requires copying the internal data interface without calling the constructor. Let’s look at the problems with these conventions:
3.1 The no-call constructor rule is too strong
The well-behaved Clone method calls the constructor to create the object, which then copies the internal data. If the class is final, Clone might even return an object created by the constructor. Since the class is final and immutable, it is more efficient to call the constructor to create an instance, or even cache it (singleton mode), and return the object when Clone is called.
3.2 x.lone ().getClass() should generally be equivalent to x.getClass(). The provision is too weak
In practice, we generally assume that if we extend a class and call super.clone in a subclass, the object returned will be an instance of that subclass (we want to clone the subclass, not the parent class).
Super.clone () does a bitwise copy(binary copy) of the original object to include references. This can cause problems. If a property inside is a mutable object, then the original object changes and the cloned object changes too. So after super.clone() is called, you usually need to copy the mutable object again.
The only way a superclass can provide this functionality is by returning an object obtained by calling super.clone. If the Clone method returns an object created by the constructor, it gets the wrong class (the current parent class instead of the desired subclass). Therefore, if you override a clone method ina non-final class, you should return an object that was obtained by calling super.clone. If all the superclasses of the class follow this rule, calling the super.clone method will eventually call the Object.clone method, creating instances of the correct class. This mechanism is similar to an automatic constructor call chain, except that it is not required.
In conclusion:
- Immutable classes should never provide clone methods
- The Cloneable structure is incompatible with the normal practice of referring to a final field of mutable objects.
- The Clone method is a shallow copy (only one layer is copied), and the objects referenced by the class need to be manually copied
Take a look at shallow and deep copy examples:
public class Student implements Cloneable{ String name; int age; public Student(String name,int age){ this.name = name; this.age = age; } public Object clone(){ Object o = null; try{ o = (Student)super.clone(); / / Object in the clone () to identify what an Object you want to copy} the catch (CloneNotSupportedException e) {System. Out. Println (e. oString ()); } return o; } public static void main(String[] args){ Student s1=new Student("zhangsan",18); Student s2=(Student)s1.clone(); System. Out. Println (" clone after s2: name = "+ s2. The name +", "+" age = "+ s2. Age); s2.name="lisi"; s2.age=20; // The value of student 1 is not affected after student 2 is changed. System. The out. Println (" cloning modified s1: name = "+ s1. The name +", "+" age = "+ s1. Age); System. The out. Println (" cloning modified s2: name = "+ s2. The name +", "+" age = "+ s2. Age); }}Copy the code
At this point, if each field of the class contains a value of a primitive type or contains a reference to an immutable object, the returned object is exactly what is needed and is simply called super.clone() without further processing. But!!!! If the object contains references to other objects, a clone cannot be completely cloned, as shown in the following example
class Professor { String name; int age; Professor(String name,int age){ this.name=name; this.age=age; } } public class Student implements Cloneable{ String name; // Constant object. int age; Professor p; // Student 1 and student 2 have the same reference value. Student(String name,int age,Professor p){ this.name=name; this.age=age; this.p=p; } public Object clone(){ Student o=null; try{ o=(Student)super.clone(); }catch(CloneNotSupportedException e){ System.out.println(e.toString()); } return o; } public static void main(String[] args){ Professor p=new Professor("wangwu",50); Student s1=new Student("zhangsan",18,p); Student s2=(Student)s1.clone(); System. The out. Println (" after cloning s1: name = "+ s1. The p.n ame +", "+" age = "+ s1. P.age); System. Out. Println (" clone after s2: name = "+ s2. The p.n ame +", "+" age = "+ s2. P.age); s2.p.name="lisi"; s2.p.age=30; System. The out. Println (" after cloning s1: name = "+ s1. The p.n ame +", "+" age = "+ s1. P.age); System. Out. Println (" clone after s2: name = "+ s2. The p.n ame +", "+" age = "+ s2. P.age); }}Copy the code
As can be seen from the results, when S2 clones S1, Professor P does not clone the attribute of S1, so that S1 and S2 reference it to the same point. Thus, if S2 changes its value, S1 will also change passively. So how can we achieve deep cloning, where professors who modify S2 don’t affect professors who modify S1? In fact, it is very simple, just need to modify Professor, as shown below to get
class Professor implements Cloneable{ String name; int age; Professor(String name,int age){ this.name=name; this.age=age; } public Object clone(){ Object o = null; try{ o = super.clone(); }catch(CloneNotSupportedException e){ System.out.println(e.toString()); } return o; }}Copy the code
Clone method o.p=(Professor) p.lone ();
public Object clone(){
Student o=null;
try{
o=(Student)super.clone();
}catch(CloneNotSupportedException e){
System.out.println(e.toString());
}
o.p=(Professor)p.clone();
return o;
}
Copy the code
To see the results as we had hoped. Therefore, when using Clone, it is important to distinguish the attributes of the object to be cloned.
4. How to implement a well-behaved Clone method
Depending on the nature of the class, the objects obtained from super.clone() can sometimes be close to the object that will eventually be returned, or far from it.
4.1 Each field contains only primitive types or references to immutable objects
In this case, we might return objects that meet our needs, such as the PhoneNumber class from Reading Notes 08. Here, we simply declare to implement the Cloneable interface, and then == provide public access to the clone method protected in Object == :
@Override public PhoneNumber clone() { try { return (PhoneNumber) super.clone(); // Covariant return types, never ask a client to do anything that the class library can do for him. } catch(CloneNotSupportedException e) { throw new AssertionError(); // Can't happen } }Copy the code
4.2 A domain contains mutable Objects
Such as the Stack class in Reading Notes 05. If you want to make the class cloneable, if its clone method simply returns super.clone(), the resulting Stack instance will have the correct size field (base type), but its elements field will reference the same array as the original Stack instance. Modifying the original instance breaks the constraints in the cloned object and vice versa. The Clone method is just another constructor, and you must make sure that it does not harm the original object and that the constraints in the cloned object are created correctly. Recursively call clone:
@Override public Stack clone() { try { Stack result = (Stack) super.clone(); result.elements = elements.clone(); // Recursively call clone. If elements is final, you need to remove final because final prevents the elements field from being assigned new values. // A clone call on an array returns an array and is compiled of the same type as the cloned array. Return result; } catch (CloneNotSupportedException e) { throw new AssertionError(); }}Copy the code
4.3 Deep copy of variables within variables
Sometimes calling Clone recursively is not enough. For example, if you implement a hash table and write the Clone method for it, its internal data contains an array of hash buckets. Each hash bucket points to the first item in the key-value pair list, or null if the bucket is empty. For performance reasons, this class implements its own lightweight one-way LinkedList instead of using Java’s internal java.util.linkedlist. The concrete class implementation is as follows:
public class HashTable implements Cloneable{
private Entry[] buckets = ...;
private static class Entry{
final Object key;
Object value;
Entry next;
Entry(Object key,Object value,Entry next){
this.key = key;
this.value = value;
this.next = next;
}
....//Remainder omitted13}
Copy the code
Suppose we just recursively clone the hash bucket array as we did for Stack, as follows:
//Broken - results in shared internal state! @Override public HashTable clone(){ try{ HashTable result = (HashTable) super.clone(); result.buckets = buckets.clone(); return result; }catch(CloneNotSupportedException e){ throw new AssertionError(); }}Copy the code
Although the cloned object has its own hash bucket array, the linked list that the array references is the same as the original object, which tends to cause uncertain behavior in the cloned object and the original object. To fix this problem, you need to copy and compose linked lists for each bucket separately. Here is a common practice:
// Recursive clone method for class with complex mutable state
public class HashTable implements Cloneable {
private Entry[] buckets = ...;
private static class Entry {
final Object key;
Object value;
Entry next;
Entry(Object key, Object value, Entry next) {
this.key = key;
this.value = value;
this.next = next;
}
// Recursively copy the linked list headed by this Entry
Entry deepCopy() {
return new Entry(key, value,
next == null ? null : next.deepCopy());
}
}
@Override public HashTable clone() {
try {
HashTable result = (HashTable) super.clone();
result.buckets = new Entry[buckets.length];
for (int i = 0; i < buckets.length; i++)
if (buckets[i] != null)
result.buckets[i] = buckets[i].deepCopy();
return result;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
... // Remainder omitted
}
Copy the code
The private class Hashtable. Entry has been enhanced to support deep copy. This approach is flexible, but if the list is long, it can easily lead to stack overflows, where each element in the list consumes a chunk of stack space. Iteration can be used instead of recursion, as follows:
//Iteratively copy the linked list headed by this Entry Entry deepCopy(){ Entry result = new Entry(key,value,next); for(Entry p = result; p.next! =null; p=p.next) p.next = new Entry(p.next.key,p.next.value,p.next.next); return result; }Copy the code
4.4 Cloning methods of complex objects
The last way to clone a complex object is to call super.clone, then set all fields in the resulting object to its blank state, and then call higher-level methods to recreate the object’s state. This method is simple, reasonable, and elegant, but is generally not as fast as the clone method, which operates directly on the internal state of an object and its clones.
5. A better way
Provide a copy constructor or copy factory instead of the Clone method copy constructor:
public class MyObject { public String field01; public MyObject() { } public MyObject(MyObject object) { this.field01 = object.field01; }}Copy the code
Copy static factory:
public class MyObject { public String field01; public MyObject() { } public static MyObject newInstance(MyObject object) { MyObject myObject = new MyObject(); myObject.field01 = object.field01; return myObject; }}Copy the code
advantages
- It doesn’t rely on a risky, out-of-language mechanism for object creation;
- It does not comply with documentation specifications that have not yet been established;
- It does not conflict with normal use of final domains;
- It does not throw unnecessary checked exceptions;
- It does not require a type conversion;
- When using it to replace clone method, the interface function features are not abandoned.
6. Summary
If the clone method must be provided:
-
1. The Clone method should not call any non-final method of the new object during construction, which will cause the state of the cloned object to be inconsistent with that of the original object.
-
The public Clone method should omit CloneNotSupportException because it is easier to use. If a clone method is overridden by a class designed specifically for inheritance, the overridden version of the Clone method should mimic the behavior of Object.clone: It should be declared protected, throw a CloneNotSupportException, and the class should not implement the Cloneable interface so that subclasses can decide for themselves whether to implement it or not.
-
Implement the Cloneable interface with a thread-safe class, bearing in mind that its Clone method must be well synchronized.
-
4. Any class that implements the Cloneable interface should override Clone with a public method. This method first calls super.clone and then fixes any fields that need fixing.
Since all the problems are related to the Cloneable interface, the new interface should not extend it, nor should new extensible classes implement it. Replication is best provided by a constructor or factory. (Divisor)
7. References
Blog. Albumenj. Cn/archives / 12… www.daimajiaoliu.com/daima/47dd7… www.javashuo.com/article/p-h… www.jianshu.com/p/acbc3fb53…
Pay attention to the public account “Programmer interview”
Reply to “interview” to get interview package!!
This public account to share their own from programmer xiao Bai to experience spring recruitment autumn recruitment cut more than 10 offer interview written test experience, including [Java], [operating system], [computer network], [design mode], [data structure and algorithm], [Dacheng face by], [database] look forward to you join!!
1. Computer network —- Three times shake hands four times wave hands
2. Dream come true —– Project self-introduction
Here are the design patterns you asked for
4. Shocked! Check out this programmer interview manual!!
5. Teach you the interview “Profile” word for word
6. Nearly 30 interviews shared