覆蓋clone時需要實現Cloneable接口,Cloneable并沒有定義任何方法。那Cloneable的意義是什么?如果一個類實現了Clonable,Object的clone方法就可以返回該對象的逐域拷貝,否則會拋出CloneNotSupportedException。
通常,實現接口是為了表明類的行為。而Cloneable接口改變了超類中PRotected方法的行為。這是種非典型用法,不值得仿效。
好了,既然覆蓋了clone方法,我們需要遵守一些約定:
另外,我們必須保證clone結果不能影響原始對象的同時保證clone方法的約定。
比如下面這種情況,沒有覆蓋clone方法,直接得到super.clone()的結果:
import java.util.Arrays;public class Stack implements Cloneable { private Object[] elements; private int size = 0; private static final int DEFAULT_INITIAL_CAPACITY = 16; public Stack() { this.elements = new Object[DEFAULT_INITIAL_CAPACITY]; } public void push(Object e) { ensureCapacity(); elements[size++] = e; } public Object pop() { if (size == 0) throw new EmptyStackException(); Object result = elements[--size]; elements[size] = null; // Eliminate obsolete reference return result; } public boolean isEmpty() { return size == 0; } // Ensure space for at least one more element. private void ensureCapacity() { if (elements.length == size) elements = Arrays.copyOf(elements, 2 * size + 1); }}結果可想而知,clone結果的elements和原始對象的elements引用同一個數組。
既然如此,覆蓋clone方法,并保證不會傷害到原始對象:
@Overridepublic Stack clone() { try { Stack result = (Stack) super.clone(); result.elements = elements.clone(); return result; } catch (CloneNotSupportedException e) { throw new AssertionError(); }}雖然把elements單獨拿出來clone了一遍,但這種做法的前提是elements不是final。其實再正常不過,clone無法和引用可變對象的不可變field兼容。
如果數組的元素是引用類型,當某個元素發生改變時仍然會出現問題。此處以Hashtable為例,Hashtable中的元素用其內部類Entry。
private static class Entry<K,V> implements Map.Entry<K,V> { int hash; final K key; V value; Entry<K,V> next; protected Entry(int hash, K key, V value, Entry<K,V> next) { this.hash = hash; this.key = key; this.value = value; this.next = next; } //..}如果像Stack例子中那樣直接對elements進行clone,某個Entry發生變化時clone出來的Hashtable也隨之發生變化。
于是Hashtable中如此覆蓋clone:
/** * Creates a shallow copy of this hashtable. All the structure of the * hashtable itself is copied, but the keys and values are not cloned. * This is a relatively expensive Operation. * * @return a clone of the hashtable */public synchronized Object clone() { try { Hashtable<K,V> t = (Hashtable<K,V>) super.clone(); t.table = new Entry[table.length]; for (int i = table.length ; i-- > 0 ; ) { t.table[i] = (table[i] != null) ? (Entry<K,V>) table[i].clone() : null; } t.keySet = null; t.entrySet = null; t.values = null; t.modCount = 0; return t; } catch (CloneNotSupportedException e) { // this shouldn't happen, since we are Cloneable throw new InternalError(); }}鑒于clone會導致諸多問題,有兩點建議:
新聞熱點
疑難解答