对于具有数组字段的类,Josh说,如果克隆方法仅返回super.clone(),则生成的类实例将在原始字段中具有正确的值,但其数组字段将引用与原始类实例相同的数组。修改原语会破坏不变量,反之亦然。
他使用了自定义堆栈实现的例子,我使用的是一个简单的学生类
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);
}
}请注意:在我的克隆方法的覆盖中,我没有在数组字段中调用克隆()。
然后我做了:
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;
}我有误解这个吗?有人能解释一下发生了什么事吗?
谢谢
~莫赫特
发布于 2014-10-17 22:43:53
通过调用s1.setMarks(new int[]{10, 10, 10});,您将创建一个全新的数组,并将其引用写入变量marks of s1。因此,s1和s2引用两个不同的数组。
如果您想使用此方法:
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});,因为这行还创建了一个新的数组引用,并删除了s1和s2之间的(数组)绑定)
这种行为可以用一个“真实世界的例子”来描述:
想象一下你和一个朋友拿着一根绳子,两头各有一个人。这条绳子代表了你所指的Array。如果你的朋友拉着绳子(改变数组中的一个值),你会注意到这一点。如果你拉着那根绳子,你的朋友也会注意到的。
通过给s1.setMarks(new int[]{...});打电话,你的朋友得到了一根新的绳子,他会放下第一根绳子。如果他拉着那根绳子,你不会注意到的,因为你们两个有不同的。通过调用s2.setMarks(new int[]{...});,您将得到一条新的绳子,并将第一根绳子掉落。这是第三个朋友,叫做Garbage Collector的信号,让他拿起绳子扔掉,因为没有人再用它了。但这位朋友有点懒,所以不能保证他会立即这么做。
发布于 2014-10-17 23:42:32
int[]类型的变量可用于封装四种不同的事物中的任何一种:
clone()方法不需要克隆第一种类型的数组,但是除了稍微增加性能成本之外,克隆这些数组可能是无害的。但是,clone()方法必须克隆第二种类型的数组,并且不要克隆第三种类型的数组。拥有第四种类型数组的对象通常不应该实现clone()。
尚不清楚是否真的希望代码将数组视为第一种类型或第三种类型;在这两种情况下,您的clone方法都不需要克隆数组。在使用数组类型变量时,第二种模式是最常见的,但您的特定用例并不适合它。
对于每个数组类型变量,确定这四种情况中的哪一种适用,并清楚地说明您应该如何继续执行clone。请注意,不能将数组分为四种类型之一,您的代码可能已经损坏,您应该在担心clone之前修复它。
https://stackoverflow.com/questions/26434278
复制相似问题