[英]Effective java Item no 74(on serialization): Implement Serializable judiciously
[英]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
。 所以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
的信号,取出绳子并将其转储,因为没有人再使用它了。 但这位朋友有点懒惰,所以无法保证他会立即这样做。
int[]
类型的变量可用于封装四种不同的东西:
永远不会被修改的数组的内容。
可能被修改的数组的内容,并且没有变量所有者不知道的引用。
可能被修改且由其他人拥有的数组的标识。
可以修改的数组的标识,该变量由变量的所有者拥有,但可能存在其他引用。
clone()
方法不需要克隆第一种类型的数组,但除了轻微的性能成本克隆之外,这样的数组很可能是无害的。 但是, clone()
方法必须克隆第二种类型的数组,并且不要克隆第三种类型的数组。 拥有第四种类型的数组的对象通常不应实现clone()
。
目前尚不清楚您是否真的希望您的代码将阵列视为第一类或第三类; 在这两种情况下,您的clone
方法都无需克隆数组。 第二种模式在使用数组类型变量时最常见,但您的特定用例不适合它。
对于每个数组类型变量,请确定这四种情况中的哪一种适用,并且将清楚如何进行clone
。 请注意,您无法将数组分类为四种类型之一,您的代码可能已损坏,您应该在担心clone
之前修复它。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.