1、概述
如果clone方法返回一个由构造器创建的对象,它就得到有错误的类。因此,如果覆盖了非final类中的clone方法,则应该返回一个通过调用super.clone得到的对象。
如果类的所有超类都遵循这条规则,那么调用super.clone最终会调用Object的clone方法,从而创建出正确的实例。这种机制类似于自动的构造器调用链,只不过它不是强制要求的。
2、示例
需求: 为HashTable编写一个clone方法,它内部数据包含一个HashTable数组,每个HashTable都指向键值对列表的第一个项,如果table是空的,返回null。
代码实现如下:
public class HashTable implements Cloneable {
private Entry[] buckets; private static class Entry{
Object key;
Object value;
Entry next; Entry(Object key, Object value, Entry next) {
this.key = key;
this.value = value;
this.next = next;
}
}
//......
} //递归clone这个HashTable数组,如下:
public HashTable clone(){
HashTable result = new HashTable();
try {
result = (HashTable) super.clone();
result.buckets = buckets.clone();
return result;
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return result;
}
虽然被clone对象有它自己的HashTable数组,但这个数组引用的链表与原始对象是一样的,从而很容易引起clone对象和原始对象中的不确定的行为,为了修正这个问题,必须单独的拷贝并组成每个桶的链表,实现如下:
public class HashTable1 implements Cloneable {
private Entry[] buckets; private static class Entry{
Object key;
Object value;
Entry next; Entry(Object key, Object value, Entry next) {
this.key = key;
this.value = value;
this.next = next;
} Entry deepCopy(){
Entry result = new Entry(key,value,next);
for(Entry p=result;p.next !=null; p=p.next){
p.next=new Entry(p.next.key,p.next.value,p.next.next);
}
return result;
}
}
//...... public HashTable1 clone(){
HashTable1 result = new HashTable1();
try {
result = (HashTable1) super.clone();
result.buckets = new Entry[buckets.length];
for(int i=0;i<buckets.length;i++){
result.buckets[i] = buckets[i].deepCopy();
}
return result;
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return result;
}
}
步骤:先调用super.clone方法,然后把结果对象中的所有域都设置称他们的空白状态,然后调用高层的方法来重新产生对象的状态。
3、总结
简而言之,所有实现了Cloneable接口的类都应该用一个公有的方法覆盖clone,此方法首先调用super.clone,然后修正任何需要修正的域。一般情况下,这意味着要拷贝任何包含内部“深层结构”的可变对象,并用指向新对象的引用代替原来指向这些对象的引用。