Cloning 分两类:影子克隆 shallow cloning
深度克隆 deep cloning
* 调用 clone() 需要 implments Cloneable。此函数为 protected,如果在外部调用,需要把它重写为 public 的。Cloneable 接口本身其实是空的,也就是没有方法需要实现。所以 clone() 可以不重写。这种空的接口被称为 marker interface 标记接口 或 tagged interface 标签接口。
1. 影子克隆是简单的克隆,对于一个对象,克隆后的对象和被克隆的对象可能有相同的属性的引用。
比如:
public class Date { private int year;
private int month;
private int day; public Date(int year, int month, int day) {
this.year = year;
this.month = month;
this.day = day;
}
} ************************************** public class Employee { private String name;
private int age;
private Date hireDate; public Employee (String name, int age, Date hireDate){
this.name = name;
this.age = age;
this.hireDate = hireDate;
} public static void main () {
Employee e1 = new Employee ("Jack", 32, new Date (13,2,30));
Employee e2 = (Employee) e1.clone();
}
}
这种情况下:
- e1 == e2 为 false
- e1.name == e2.name 为 true
- e1.hireDate == e2.hireDate 为 true
* 所以,e1和e2虽然是不同的引用,但他们的属性 name 和 hireDate 却是同一个引用。
当修改e2时,
- e2.age 的修改不影响 e1.age
- e2.name 的修改不影响 e1.name ,因为 String 是 final 类,是不可变的,e2.name 的修改自动为其创建了一个新的 String。
- e2.hireDate 的修改会影响 e1.hireDate (ex. e2.hireDate.setYear(10)),执行完此语句后,e1.hireDate 的 year 变成了 10。
这可能是我们不想看到的,对克隆的修改不应该影响本体的值。
这种情况就应该使用 深度克隆。
2. 深度克隆
深度克隆实际上就是在类中重写 clone(),使得以上的问题得以避免。
实现方法比如:
@Override
protected Object clone() throws CloneNotSupportedException {
Employee emp = (Employee) super.clone();
if (hireDate != null) // no point cloning a null object (one that does not exist)
emp.hireDate = new Date(hireDate.year, hireDate.month, hireDate.day);
return emp;
}
新建的 Date 让 e1 和 e2 的 hireDate 区分开来,互不影响了。
所以当我们想用 clone() 函数时,如果类中包含的属性有其他类的类型,一定要重写 clone()。