Cloneable的通用约定有三:
x.clone() != x 为true。x.clone().getClass() == x.getClass() 为true。 但这不是绝对的要求。x.clone().equals(x) 为true。 但这也不是绝对的要求。想像下面这个ABC类,包含了A,B,C三个类型的域。copy()方法创建一个新对象,然后逐域拷贝引用,这是一个标准的浅拷贝,但它却满足Cloneable接口的通用约定。
public class ABC {
A a;
B b;
C c;
public ABC(A a, B b, C c) {
this.a = a;
this.b = b;
this.c = c;
}
public ABC copy(ABC o) { // 这个copy完全执行浅拷贝,但返回对象满足Cloneable接口的约定
return new ABC(o.a, o.b, o.c);
}
}
Object类源码中的注释部分如下:
First, if the class of this object does not implement the interface {@code Cloneable}, then a code CloneNotSupportedException is thrown. Otherwise, this method creates a new instance of the class of this object and initializes all its fields with exactly the contents of the corresponding fields of this object, as if by assignment; the contents of the fields are not themselves cloned. Thus, this method performs a “shallow copy” of this object, not a “deep copy” operation. Note that all arrays are considered to implement the interface {@code Cloneable}
总结起来就是三点:
Clonable接口,调用clone()方法会抛出CloneNotSupportedException异常。Clonable接口的类,clone()方法需要执行一个浅拷贝。Clonable接口。clone()方法只做 浅拷贝 就可以满足Cloneable接口的约定。 浅拷贝 都是复制对象,而不是内容。
浅拷贝 的情况,都是 深拷贝。String和Enum,尤其是Enum应该绝对禁止拷贝。Cloneable接口。下面的代码展示了一个类实现Cloneable接口的惯用法。
Cloneable接口可变对象域,递归实现Cloneable接口这样实现的结果,也只能说尽可能地接近深拷贝。因为其中的不可变域还是浅拷贝。但这样的浅拷贝并不影响实际的使用。
public class Person implements Cloneable {
private final String name; // String is not Cloneable
private final int age;
private final Sex sex; // Enum is not Cloneable
private final PhoneNumber telephone;
// ... some other code here
@Override
public Object clone() {
try {
return (Person)super.clone();
} catch (CloneNotSupportedException e) { // cannot happen
return new RuntimeException(e);
}
}
}
public class PhoneNumber implements Cloneable {
private final short areaCode;
private final short prefix;
private final short lineNumber;
@Override
public Object clone() {
try {
return (PhoneNumber)super.clone();
} catch (CloneNotSupportedException e) { // cannot happen
return new RuntimeException(e);
}
}
}
当一个类要实现一个行为良好的clone()方法,如果它的所有超类都递归调用了super.clone()方法,并最终追溯到最终超级父类Object的clone()方法,那么Object的clone()方法总能复制并返回一个正确的类型。因此,这个类也可以调用super.clone()方法就能获得一个正确的拷贝对象。
但如果中间哪一个类没有调用super.clone()方法,而是使用了自己的构造器返回克隆的实例,那么它的所有子类就都不能获得Object的clone()方法提供的良好服务。而Object的clone()方法是行为良好的clone()方法的有力保障。所以,尽量保持自动向上构造器调用链的畅通。
具体参考下面这个例子,Person,Employee,Manager三个类层层继承。Employee比Person多了一个表示职位的域position,Manager又比Employee多了一个表示持有公司股票份额的域stock。这三个类的clone()方法异常简单,都只是向上转达了对super.clone()的调用。而最后实际执行工作的Object#clone()能够识别出调用对象mg运行时的实际类型,并且成功拷贝了Manager的所有四个域。将任务交给Object#clone()来做,事情就变得简单许多。
package com.ciaoshen.effectivejava.chapter3;
public class TestConstructor {
private static class Person implements Cloneable {
protected final String name;
protected short age;
public Person(String name, int age) {
this.name = name;
this.age = (short)age;
}
public String toString() {
return "Person[" + name + ", " + age + "]";
}
public Person clone() {
try {
return (Person)super.clone(); // 最后实际执行任务的是Object#clone()方法,它识别出实际类型为Manager,然后逐域拷贝。
} catch(CloneNotSupportedException e) {
throw new RuntimeException(e); // never happen
}
}
}
private static class Employee extends Person implements Cloneable {
protected String position;
public Employee(String name, int age, String position) {
super(name,age);
this.position = position;
}
public String toString() {
return "Employee[" + name + ", " + age + ", " + position + "]";
}
public Employee clone() {
return (Employee)super.clone();
}
}
private static class Manager extends Employee implements Cloneable {
protected int stock;
public Manager(String name, int age, String position, int stock) {
super(name,age,position);
this.stock = stock;
}
public String toString() {
return "Manager[" + name + ", " + age + ", " + position + ", " + stock + "]";
}
public Manager clone() {
return (Manager)super.clone();
}
}
public static void main(String[] args) {
Manager mg = new Manager("Ronald", 30, "Chef de project", 10000);
Manager mgCopy = mg.clone(); // 最终追溯到Object#clone()
System.out.println(mgCopy);
}
}
和静态工厂一样,为复杂对象的拷贝提供一个拷贝工厂也可以让事情变得更简单明了。
Cloneable接口。Cloneable接口。Cloneable接口和clone()方法。