《Beginning Java 7》 - 2 - Cloning 克隆

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()。

posted @ 2013-12-24 08:04  davesuen  阅读(254)  评论(0编辑  收藏  举报