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,然后修正任何需要修正的域。一般情况下,这意味着要拷贝任何包含内部“深层结构”的可变对象,并用指向新对象的引用代替原来指向这些对象的引用。