JDK 版本:1.8
代碼位址
1.前言
clone方法能友善的獲得一個對象的拷貝,但其中也有些細節需要注意。
2.實作注意事項
2.1 要調用 clone 方法必須實作 Cloneable 接口
如果類沒有實作 Cloneable 接口,類的執行個體卻調用了 clone 方法,則會抛出CloneNotSupportedException異常。
用反射機制去調用 Objec 類的 clone 方法,可以觀察到這個現象:
Object object = new Object();
Method method = object.getClass().getDeclaredMethod("clone");
method.setAccessible(true);
method.invoke(object);
運作結果:
Caused by: java.lang.CloneNotSupportedException: java.lang.Object
at java.lang.Object.clone(Native Method)
2.2 clone 方法的預設實作是淺拷貝
clone 方法的預設實作是拷貝,即對于類中可變對象沒有進行真正的拷貝,隻是将該可變對象指派給了克隆對象對應的字段。修改目前對象中可變對象也會影響到克隆對象中相應的字段。
舉個例子來說明下:
private static void shallowCopyTest() {
Person ming = new Person("ming", new String[]{"mingMing", "daMing"});
compareCloneObject(ming);
}
private static void compareCloneObject(Person person) {
try {
Person fakePerson = (Person) person.clone();
System.out.println("person.getNickName() == fakePerson.getNickName() : "
+ (person.getNickName() == fakePerson.getNickName()));
fakePerson.setNickName(0, "compare");
System.out.println(person);
System.out.println(fakePerson);
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
private static class Person implements Cloneable {
private String name;
private String[] nickName;
public Person(String name, String[] nickName) {
this.name = name;
this.nickName = nickName;
}
public void setNickName(int index, String nickName) {
this.nickName[index] = nickName;
}
public void setNickName(String[] nickName) {
this.nickName = nickName;
}
public String[] getNickName() {
return nickName;
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", nickName=" + Arrays.toString(nickName) +
'}';
}
}
調用shallowCopyTest方法,輸出結果為:
person.getNickName() == fakePerson.getNickName() : true
Person{name='ming', nickName=[compare, daMing]}
Person{name='ming', nickName=[compare, daMing]}
可以看到原對象與克隆對象的 nickName 是同一個數組,更改一個以後另一個的值也發送相應變化。
2.3 深拷貝的實作
實作深拷貝要将對象類的所有可變對象都拷貝一遍,然後用拷貝的引用去替換掉原有的引用。
實作例子如下:
private static void deepCopyTest() {
Student ming = new Student("ming", new String[]{"mingMing", "daMing"}, "jiangNan");
compareCloneObject(ming);
}
public static class Student extends Person {
private String school;
public Student(String name, String[] nickName, String school) {
super(name, nickName);
this.school = school;
}
@Override
public Object clone() throws CloneNotSupportedException {
Student student = (Student) super.clone();
student.setNickName(this.getNickName().clone());
return student;
}
@Override
public String toString() {
return "Student{" +
"name='" + super.name + '\'' +
", nickName=" + Arrays.toString(super.nickName) +
", school='" + school + '\'' +
'}';
}
}
調用deepCopyTest方法,輸出結果為:
person.getNickName() == fakePerson.getNickName() : false
Student{name='ming', nickName=[mingMing, daMing], school='jiangNan'}
Student{name='ming', nickName=[compare, daMing], school='jiangNan'}
可以看到兩個對象 nickName 已經不是同一個對象了,改變之後也不會互相影響了。這是因為在覆寫的 clone 方法中對 nickName 數組也進行了克隆,并指派給克隆對象。
3.注釋文檔及個人翻譯
/**
* 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 {@code x}, the expression:
*
* * x.clone() != x
* will be true, and that the expression:
*
* * x.clone().getClass() == x.getClass()
* will be {@code true}, but these are not absolute requirements.
* While it is typically the case that:
*
* * x.clone().equals(x)
* will be {@code true}, this is not an absolute requirement.
*
* 建立并傳回一個對象的拷貝。拷貝的準确定義取決于對象的類定義。普遍的來說,對任意對象 x,
* 表達式:x.clone() ! = x 的值是 true,并且表達式:x.clone().getClass() == x.getClass()
* 的值是
* true,但這些要求并不是絕對的。并且通常表達式:x.clone().equals(x)的值也是 true,
* 這也不是絕對必要的條件。
*
* By convention, the returned object should be obtained by calling
* {@code super.clone}. If a class and all of its superclasses (except
* {@code Object}) obey this convention, it will be the case that
* {@code x.clone().getClass() == x.getClass()}.
*
* 按照慣例,應該通過調用 super.clone 來擷取傳回的對象。如果一個類及其所有超類(Object 除外)
* 都遵循此約定,則 x.clone().getClass() == x.getClass() 成立。
*
* By convention, the object returned by this method should be independent
* of this object (which is being cloned). To achieve this independence,
* it may be necessary to modify one or more fields of the object returned
* by {@code super.clone} before returning it. Typically, this means
* copying any mutable objects that comprise the internal "deep structure"
* of the object being cloned and replacing the references to these
* objects with references to the copies. If a class contains only
* primitive fields or references to immutable objects, then it is usually
* the case that no fields in the object returned by {@code super.clone}
* need to be modified.
*
* 按照慣例,被本方法傳回的對象應該與被克隆的原本對象無關。為了實作這種獨立,可能需要在
* 克隆方法傳回前修改被傳回對象的字段。通常,這意味着拷貝構成被克隆對象的内部“深度結構”
* 的任何可變對象,并用拷貝的引用替換掉原本對象的引用。如果一個類僅僅包含原始字段或不可
* 變對象的引用,那麼通常super.clone傳回對象中沒有字段需要被修改。
*
* The method {@code clone} for class {@code Object} performs a
* specific cloning operation. First, if the class of this object does
* not implement the interface {@code Cloneable}, then a
* {@code CloneNotSupportedException} is thrown. Note that all arrays
* are considered to implement the interface {@code Cloneable} and that
* the return type of the {@code clone} method of an array type {@code T[]}
* is {@code T[]} where T is any reference or primitive type.
* Otherwise, this method creates a new instance of the class of this
* object and initializes all its fields with exactly the contents of
* the corresponding fields of this object, as if by assignment; the
* contents of the fields are not themselves cloned. Thus, this method
* performs a "shallow copy" of this object, not a "deep copy" operation.
*
* Object 類的 clone 方法執行特定的克隆操作。首先,如果對象的類沒有實作 Cloneable
* 接口,那麼會抛出 CloneNotSupportedException 異常。請注意,所有的數組都被視為實
* 現了 Cloneable 接口,并且 T[] 類型數組 clone 接口的傳回類型是 T[],此處 T 可以
* 是任意引用類型或原始類型。除此之外,此方法建立一個對象的類的新執行個體,然後用原本對象對
* 應字段内容來初始化新執行個體所有字段,就像指派;字段的内容不會被克隆。是以,此方法對對象
* 執行了“淺拷貝”而不是一個“深拷貝”。
*
* The class {@code Object} does not itself implement the interface
* {@code Cloneable}, so calling the {@code clone} method on an object
* whose class is {@code Object} will result in throwing an
* exception at run time.
* Object 類自身沒有實作 Cloneable 接口,是以當對一個 Object 類的執行個體調用 clone
* 方法時将會在運作時抛出異常。
* @return a clone of this instance.
* @throws CloneNotSupportedException if the object's class does not
* support the {@code Cloneable} interface. Subclasses
* that override the {@code clone} method can also
* throw this exception to indicate that an instance cannot
* be cloned.
* @see java.lang.Cloneable
*/
protected native Object clone() throws CloneNotSupportedException;