簡體   English   中英

如何使用clone()方法克隆Java對象

[英]How to clone a Java object with the clone() method

我不明白克隆自定義對象的機制。 例如:

public class Main{

    public static void main(String [] args) {

        Person person = new Person();
        person.setFname("Bill");
        person.setLname("Hook");

        Person cloned = (Person)person.clone();
        System.out.println(cloned.getFname() + " " + cloned.getLname());
    }
}

class Person implements Cloneable{

    private String fname;
    private String lname;

    public Object clone() {

        Person person = new Person();
        person.setFname(this.fname);
        person.setLname(this.lname);
        return person;
    }

    public void setFname(String fname) {
        this.fname = fname;
    }

    public void setLname(String lname){
        this.lname = lname;
    }

    public String getFname(){
        return fname;
    }

    public String getLname() {
        return lname;
    }
}

這個示例顯示了正確的克隆方式,如書中所寫。 但是我可以在類名定義中刪除implements Cloneable,並且我收到相同的結果。

所以我不明白Cloneable的提議以及為什么在類Object中定義了clone()方法?

克隆方法旨在進行深層復制。 確保您了解深拷貝和淺拷貝之間的區別。 在您的情況下,復制構造函數可能是您想要的模式。 在某些情況下,您無法使用此模式,例如,因為您正在為類X創建子類,並且您無法訪問所需的X構造函數。 如果X正確覆蓋其克隆方法(如有必要),則可以按以下方式復制:

class Y extends X implements Cloneable {

    private SomeType field;    // a field that needs copying in order to get a deep copy of a Y object

    ...

    @Override
    public Y clone() {
        final Y clone;
        try {
            clone = (Y) super.clone();
        }
        catch (CloneNotSupportedException ex) {
            throw new RuntimeException("superclass messed up", ex);
        }
        clone.field = this.field.clone();
        return clone;
    }

}

通常在覆蓋克隆方法時:

  • 使返回類型更具體
  • 從調用super.clone()
  • 當你知道clone()也適用於任何子類時(克隆模式的弱點;如果可能的話,make class final),不要包含throws子句
  • 保留不可變和原始字段,但在調用super.clone()之后手動克隆可變對象字段(克隆模式的另一個弱點,因為這些字段不能成為最終字段)

Objectclone()方法(最終將在所有超類遵循契約時調用)生成淺層副本並處理新對象的正確運行時類型。 請注意在整個過程中如何調用構造函數。

如果您希望能夠在實例上調用clone() ,則實現Cloneable接口並將該方法設為public。 如果您不希望能夠在實例上調用它,但您確實希望確保子類可以調用它們的super.clone()並獲得它們所需的內容,那么請不要實現Cloneable並保持方法protected超類還沒有宣布它是公開的。

克隆模式很難並且有很多陷阱。 確保它是你需要的。 考慮復制構造函數或靜態工廠方法。

Object類中的clone()執行內存的淺表副本,而不是像構造函數那樣調用方法。 為了在任何不實現clone()本身的對象上調用clone() ,您需要實現Clonable接口。

如果重寫clone()方法,則不必實現該接口。

正如JavaDoc所說,這將導致異常:

class A {
  private StringBuilder sb; //just some arbitrary member
}

...

new A().clone(); //this will result in an exception, since A does neither implement Clonable nor override clone()

如果A在例如將實現Clonable調用clone()Object版本)將導致新的A引用同樣的 StringBuilder實例,即改變sb在克隆的情況下會導致更改sb在原A實例。

這意味着一個淺拷貝,這是為什么通常更好地覆蓋clone()一個原因。

編輯:正如旁注,使用返回類型協方差將使您的重寫clone()更明確:

public Person clone() {
  ...
}

JVM能夠為您克隆對象,因此您不應該自己構建新人。 只需使用此代碼:

class Person implements Cloneable {
    // ...
    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

要么

class Person implements Cloneable {
    // ...
    @Override
    public Object clone() {
        try {
            return super.clone();
        }
        catch (CloneNotSupportedException e) {
            throw new Error("Something impossible just happened");
        }
    }
}

即使Person類是子類,這也會起作用,而您的克隆實現將始終創建Person的實例(例如,不是Employee的Employee實例)。

克里斯並不成熟,約書亞布洛赫說:

http://www.artima.com/intv/bloch13.html

這里沒有必要在clone()方法中顯式創建對象。 只需調用super.clone()創建此對象的副本。 它將執行淺層克隆。

它與任何此類界面的目的相同。 它主要允許方法(等)接受任何Clonable對象,並且可以訪問他們需要的方法,而不必將自己限制在一個特定的對象上。 誠然,Clonable可能是這方面不太有用的接口之一,但肯定有你可能需要它的地方。 如果你想要更多的想法,可以考慮接口Comparable,例如允許你有排序列表(因為列表不需要知道對象是什么,只是可以比較它們)。

如果你沒有聲明克隆接口,那么在調用clone方法時你應該得到CloneNotSupportException。如果你聲明然后調用clone方法,它將進行淺拷貝。

在您的示例中,您沒有進行實際克隆。您將覆蓋對象類的clone()方法並給出您自己的實現。 但是在你的克隆方法中,你正在創建一個新的Person對象。 並返回它。在這種情況下,實際對象不會被克隆。

所以你的克隆方法應該是這樣的:

public Object clone() {
      return super.clone();
  }

所以這里克隆將由超類方法處理。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM