当我们想要克隆一个对象时,也就是说我们想要两个一模一样的对象,但想要这两个对象各自开辟空间的时候,我们通常要实现cloneable接口。但是接口没给我们提供clone的实现,但Object类却给我们提供了一个受保护的clone实现。
一般来讲我们直接在子类重写的clone方法调用super,clone()可以得到我们想要的结果。
import java.util.Date;
public class User implements Cloneable {
private String username;
private String password;
private Date birthdate;
public User(String username, String password, Date birthdate) {
this.username = username;
this.password = password;
this.birthdate = birthdate;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public int hashCode() {
// 省略equals的实现(可用eclipse自动生成)
}
@Override
public boolean equals(Object obj) {
// 省略equals的实现(可用eclipse自动生成)
}
// 省略一大堆get/set方法
}
import java.util.Date;
import org.junit.Test;
public class TestCase {
@Test
public void testUserClone() throws CloneNotSupportedException {
User u1 = new User("Kent", "123456", new Date());
User u2 = u1;
User u3 = (User) u1.clone();
System.out.println(u1 == u2); // true
System.out.println(u1.equals(u2)); // true
System.out.println(u1 == u3); // false
System.out.println(u1.equals(u3)); // true
}
}
如上诉例子所示,我们得到了我们预期的结果,但也有时结果会和预期相去甚远。
public class Administrator implements Cloneable {
private User user;
private Boolean editable;
public Administrator(User user, Boolean editable) {
this.user = user;
this.editable = editable;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public int hashCode() {
// 老规矩
}
@Override
public boolean equals(Object obj) {
// 老规矩
}
// 老规矩
}
import java.util.Date;
import org.junit.Test;
public class TestCase {
@Test
public void testAdministratorClone() throws CloneNotSupportedException {
Administrator a1 = new Administrator(new User("Kent", "123456", new Date()), true);
Administrator a2 = a1;
Administrator a3 = (Administrator) a1.clone();
System.out.println(a1 == a2); // true
System.out.println(a1.equals(a2)); // true
System.out.println(a1 == a3); // false
System.out.println(a1.equals(a3)); // true
System.out.println(a1.getUser() == a3.getUser()); //true ! It's not our expected!!!!!
System.out.println(a1.getUser().equals(a3.getUser())); //true
}
}
这里我们就可以引入两个专业的术语:浅克隆(shallow clone)和深克隆(deep clone)。
所谓的浅克隆,顾名思义就是很表面的很表层的克隆,如果我们要克隆Administrator对象,只克隆他自身以及他包含的所有对象的引用地址。
而深克隆,就是非浅克隆。克隆除自身以外所有的对象,包括自身所包含的所有对象实例。至于深克隆的层次,由具体的需求决定,也有“N层克隆”一说。
但是,所有的基本(primitive)类型数据,无论是浅克隆还是深克隆,都会进行原值克隆。毕竟他们都不是对象,不是存储在堆中。注意:基本数据类型并不包括他们对应的包装类。
如果我们想让对象进行深度克隆,我们可以这样修改Administrator类。
@Override
protected Object clone() throws CloneNotSupportedException {
Administrator admin = (Administrator) super.clone();
admin.user = (User) admin.user.clone();
return admin;
}
简而言之,所有实现了Cloneable接口的类都应该有一个公有的类覆盖clone。此方法先调用super. clone,然后修正需要修正的域。cloneable有很多问题,我们可以使用拷贝构造器或者拷贝工厂去代替它。
public Yum(Yum yum);
public static Yum newInstance(Yum yum)
这两个做法都比实现Cloneable接口具有更多优势。事实上许多专家级的程序员重来不去覆盖clone()方法也重来不去调用它。