有效的Java项目11:明智地覆盖克隆

EMM*_*EMM 3 java cloning effective-java

对于具有数组字段的类,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);
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意:我没有在克隆方法的覆盖中调用我的数组字段上的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);
    }
}
Run Code Online (Sandbox Code Playgroud)

输出:

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]
Run Code Online (Sandbox Code Playgroud)

我想知道在原始实例中更改数组也会改变我的克隆中的数组,因为我在上面提到"数组字段将引用与原始实例相同的数组"

随着我的克隆实现,我也应该看到克隆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;
    }
Run Code Online (Sandbox Code Playgroud)

我误解了吗?有人可以解释一下发生了什么吗?


谢谢
〜莫希特

Tom*_*Tom 10

通过调用s1.setMarks(new int[]{10, 10, 10});你创建一个全新的阵列和写它的参考变量markss1.所以s1s2参考两个不同的数组.

如果你有这个方法:

public void setMark(int mark, int pos) {
    marks[pos] = mark;
}
Run Code Online (Sandbox Code Playgroud)

在类中Student并执行以下代码:

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

然后你会看到,这也会影响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]
Run Code Online (Sandbox Code Playgroud)

(不要忘记对该行进行注释s2.setMarks(new int[]{10, 29, 30});,因为此行还会创建一个新的数组引用并删除(s1s2)之间的(数组)绑定

可以使用"真实世界示例"来描述此行为:

图片你和一个朋友正拿着一根绳子,两端各有一个人.这条绳子代表Array你们所指的.如果你的朋友拉绳子(更改该阵列中的值),你会注意到这一点.如果你拉绳子,你的朋友也会注意到.

通过打电话给s1.setMarks(new int[]{...});你的朋友得到一根新绳子,他会丢掉第一根绳子.如果他拉绳子,你就不会注意到,因为你们两个人有不同的绳子.通过呼叫s2.setMarks(new int[]{...});你将获得一根新绳子并放下第一根绳子.这是第三个朋友的信号,叫做Garbage Collector绳索并将其丢弃,因为没有人再使用它了.但这位朋友有点懒惰,所以无法保证他会立即这样做.