簡體   English   中英

有效的Java項目11:明智地覆蓋克隆

[英]Effective Java Item 11: Override clone Judiciously

對於具有數組字段的類,Josh說如果clone方法只返回super.clone(),則生成的類實例將在原始字段中具有正確的值,但其數組字段將引用與原始類實例相同的數組。 修改原始內容會破壞不變量,反之亦然。

他使用了自定義Stack實現的例子,我使用的是一個簡單的Student類

class Student implements Cloneable {
    private String name;
    private int age;
    private int[] marks = {90, 70, 80};

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void setMarks(int[] marks) {
        this.marks = marks;
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    protected Student clone() throws CloneNotSupportedException {
        return (Student) super.clone();
    }

    @Override
    public String toString() {
        return "Student - Name : " + name + " Age : " + age + " Marks : " + Arrays.toString(marks);
    }
}

請注意:我沒有在克隆方法的覆蓋中調用我的數組字段上的clone()。

然后我做了:

public class CloningDemo {
    public static void main(String[] args) {
        Student s1 = new Student("Mohit", 30);
        Student s2 = null;
        try {
            s2 = s1.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        System.out.println("S1 : " + s1);
        System.out.println("S2 : " + s2);
        System.out.println("Updating the clone...");
        s2.setName("Rohit");
        s2.setAge(29);
        s2.setMarks(new int[]{10, 29, 30});
        System.out.println("S1 : " + s1);
        System.out.println("S2 : " + s2);
        System.out.println("Updating the array elements in Original...");
        s1.setMarks(new int[]{10, 10, 10});
        System.out.println("S1 : " + s1);
        System.out.println("S2 : " + s2);
    }
}

輸出:

S1 : Student - Name : Mohit Age : 30 Marks : [90, 70, 80]
S2 : Student - Name : Mohit Age : 30 Marks : [90, 70, 80]
Updating the clone...
S1 : Student - Name : Mohit Age : 30 Marks : [90, 70, 80]
S2 : Student - Name : Rohit Age : 29 Marks : [10, 29, 30]
Updating the array elements in Original...
S1 : Student - Name : Mohit Age : 30 Marks : [10, 10, 10]
S2 : Student - Name : Rohit Age : 29 Marks : [10, 29, 30]

我想知道在原始實例中更改數組也會改變我的克隆中的數組,因為我在上面提到“數組字段將引用與原始實例相同的數組”

隨着我的克隆實現,我也應該看到克隆s2的變化。 正確的實施將是:

@Override
    protected Student clone() throws CloneNotSupportedException {
        Student student = (Student) super.clone();
        student.marks = marks.clone();  // I am not doing this in my code.
        return student;
    }

我誤解了嗎? 有人可以解釋一下發生了什么嗎?


謝謝
〜莫希特

通過調用s1.setMarks(new int[]{10, 10, 10}); 你正在創建一個全新的數組並將其引用寫入s1的變量marks 所以s1s2指的是兩個不同的數組。

如果你有這個方法:

public void setMark(int mark, int pos) {
    marks[pos] = mark;
}

在類Student執行以下代碼:

System.out.println("Updating the array element in Original...");
s1.setMark(999, 0);
System.out.println("S1 : " + s1);
System.out.println("S2 : " + s2);

然后你會看到,這也影響了s2

Updating the array elements in Original...
S1 : Student - Name : Mohit Age : 30 Marks : [999, 70, 80]
S2 : Student - Name : Rohit Age : 29 Marks : [999, 70, 80]

(不要忘記注釋行s2.setMarks(new int[]{10, 29, 30});以及,因為這行也創建了一個新的數組引用並刪除了s1s2之間的(數組)綁定)

可以使用“真實世界示例”來描述此行為:

圖片你和一個朋友正拿着一根繩子,兩端各有一個人。 這根繩子代表你所指的Array 如果你的朋友拉繩子(更改該陣列中的值),你會注意到。 如果你拉繩子,你的朋友也會注意到。

通過調用s1.setMarks(new int[]{...}); 你的朋友得到一根新繩子,他會丟掉第一根繩子。 如果他拉繩子,你就不會注意到,因為你們兩個人有不同的繩子。 通過調用s2.setMarks(new int[]{...}); 你會得到一根新繩子並放下第一根繩子。 這是第三個朋友,稱為Garbage Collector的信號,取出繩子並將其轉儲,因為沒有人再使用它了。 但這位朋友有點懶惰,所以無法保證他會立即這樣做。

int[]類型的變量可用於封裝四種不同的東西:

  1. 永遠不會被修改的數組的內容。

  2. 可能被修改的數組的內容,並且沒有變量所有者不知道的引用。

  3. 可能被修改且由其他人擁有的數組的標識。

  4. 可以修改的數組的標識,該變量由變量的所有者擁有,但可能存在其他引用。

clone()方法不需要克隆第一種類型的數組,但除了輕微的性能成本克隆之外,這樣的數組很可能是無害的。 但是, clone()方法必須克隆第二種類型的數組,並且不要克隆第三種類型的數組。 擁有第四種類型的數組的對象通常不應實現clone()

目前尚不清楚您是否真的希望您的代碼將陣列視為第一類或第三類; 在這兩種情況下,您的clone方法都無需克隆數組。 第二種模式在使用數組類型變量時最常見,但您的特定用例不適合它。

對於每個數組類型變量,請確定這四種情況中的哪一種適用,並且將清楚如何進行clone 請注意,您無法將數組分類為四種類型之一,您的代碼可能已損壞,您應該在擔心clone之前修復它。

暫無
暫無

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

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