有效的Java项目11:明智地覆盖克隆 [英] Effective Java Item 11: Override clone Judiciously

查看:181
本文介绍了有效的Java项目11:明智地覆盖克隆的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

对于带有数组字段的类,Josh说如果clone方法只返回super.clone(),则生成的类实例将在原始字段中具有正确的值,但其数组字段将引用与原始字段相同的数组类实例。修改原始文件会破坏不变量,反之亦然。

For a class with an array field Josh says if the clone method merely return super.clone(), the resulting class instance will have the correct values in primitive fields, but its array field will refer to the same array as the original class instance. Modifying the original will destroy the invariants and vice-versa.

他使用了自定义Stack实现的例子,我使用的是一个简单的Student类

He used the example of custom Stack implementation, I am using a simple Student class

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]

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

I was wondering that changing array in original instance would change the array in my clone too, because I mentioned above "array field will refer to the same array as the original instance"

通过我的克隆实现,我也应该看到克隆s2的变化。正确的实现将是:

With my implementation of clone I should have seeing changes in the clone s2 too. The proper implementation would've been:

@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;
    }

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

Have I misunderstood this? Can someone please explain what is going on?


谢谢

~Mohit


Thanks
~Mohit

推荐答案

通过调用 s1.setMarks(new int [] {10,10,10}); 你正在完全创建一个新数组并将其引用写入标记 s1 。所以 s1 s2 引用两个不同的数组。

By calling s1.setMarks(new int[]{10, 10, 10}); you're creating a completely new array and write its reference to the variable marks of s1. So s1 and s2 refer to two different arrays.

如果你有这个方法:

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

班级学生并执行以下代码:

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

然后你会看到,这会影响 s2 也是:

then you will see, that this affects s2 too:

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}); 同样,因为这一行还创建了一个新的数组引用并删除了 s1 s2 之间的(数组)绑定)

(don't forget to comment the line s2.setMarks(new int[]{10, 29, 30});, as well, because this line also creates a new array reference and removes the (array) binding between s1 and s2)

这个行为可以用真实世界的例子来描述:

This behavior could be described with a "real world example":

你和朋友的形象是拿着绳子,两端各有一个人。此绳索代表您所指的数组。如果你的朋友拉绳子(更改该阵列中的值),你会注意到这一点。如果你拉绳子,你的朋友也会注意到。

Image you and a friend are holding a rope, with one person on each end. This rope represents the Array you're both referring to. If your friend pulls that rope (changing a value in that array), you'll notice that. And if you pull that rope, your friend will notice that, too.

通过调用 s1.setMarks(new int [] {... }); 你的朋友得到一根新绳子,他会丢掉第一根绳子。如果他拉绳子,你就不会注意到,因为你们两个人有不同的绳子。通过调用 s2.setMarks(new int [] {...}); ,您将获得一条新绳索并放弃第一条绳索。这是第三个朋友的信号,叫做垃圾收集器,拿走那根绳子并将其转储,因为没有人再使用它了。但这位朋友有点懒惰,所以无法保证他会立即这样做。

By calling s1.setMarks(new int[]{...}); your friend gets a new rope and he will drop the first one for it. If he pulls that rope, you won't notice that, because you two have different ones. By calling s2.setMarks(new int[]{...}); you will get a new rope and drop the first one, as well. This is the signal for the third friend, called Garbage Collector, to take that rope and dump it, because no one is using it anymore. But this friend is kind of lazy, so there is no guarantee that he will do that immediately.

这篇关于有效的Java项目11:明智地覆盖克隆的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆