Clone基本知識儲備
在Java裡提到clone技術,就不能不提Java.lang.Cloneable接口和含有clone方法的Object類。所有具有clone功能的類都有一個特性,那就是它直接或間接地實現了Cloneable接口。否則,我們在嘗試調用clone()方法時,將會觸發CloneNotSupportedException異常。下面我們通過對Object類的部分源碼的分析,來發現和理解這一特性。請看JDK中Object# clone()方法的源碼:
/*
…………
* @return a clone of this instance.
* @exception? CloneNotSupportedException? if the object''s class does not
*support the Cloneable interface. Subclasses
*that override the clone method can also
* throw this exception to indicate that an instance cannot
*be cloned.
* @see Java.lang.Cloneable
*/
protected native Object clone() throws CloneNotSupportedException;
這段源碼的@exception部分的描述內容證實了上文關於clone對象特性論斷的正確性。它明確指出對象類必須支持Cloneable接口,否則即使派生類覆蓋了Object#clone()方法,也同樣會拋出CloneNotSupportedException這個異常。關於覆蓋clone()方法,後續文章將會用專門篇幅進行比較詳細的分析.
在上一篇中,介紹了Java裡clone的基本知識。本篇將著重描述如何實現clone。
l clone的實現
1.實現Cloneable接口
通過上一篇的介紹,我們知道,一個類若要具備clone功能,就必須實現Cloneable接口。做到這一步,clone功能已經基本實現了。Clone功能對我們來說,最主要的還是要能夠使用它。那麼我們如何才能使用clone功能呢?答案是覆蓋Object#clone()方法。
2. 覆蓋Object#clone()方法
為什麼需要覆蓋Object#clone()方法?這裡得再次從jdk源碼說起。JDK中Object# clone()方法的原型是:
protected native Object clone() throws CloneNotSupportedException;
是否注意到,這裡clone()方法修飾符是protected,而不是public。這種訪問的不可見性使得我們對Object#clone()方法不可見。相信讀者已明白為什麼要覆蓋Object#clone()方法。而且,覆蓋的方法的修飾符必須是public,如果還保留為protected,覆蓋將變得沒有實際意義。下面舉一個具有clone功能的簡單的例子: /*
* 具有clone功能的類的例子
*/
public class CloneableObjExample implements Cloneable {
//……部分代碼已省略……
private String name = null;
private int score = 0;
/**
* NOTE: 將protected 修飾符 更改為 public
* @see Java.lang.Object#clone()
*/
public/*protected*/ Object clone() throws CloneNotSupportedException {
// call父類的clone方法
Object result = super.clone();
//TODO: 定制clone數據
return result;
}
}
3.定制clone
至此,clone已經真相大白。Clone的對象我們可以對其進行定制。還就上面的例子來說。下面的方法對功能做了一定的增強:
public/*protected*/ Object clone() throws CloneNotSupportedException {
// call父類的clone方法
CloneableObjExample result = (CloneableObjExample)super.clone();
//TODO: 定制clone數據
//雖然”clone”了,但還可以做點調整
result.name = “New Name”;
result.score = 90;
return result;
}
本篇介紹了如何實現clone。接下來的篇幅將就縱深clone等clone的高級特性進行分析。
本章將進入clone的高級特性,著重講述縱深clone技術。
Clone通常有兩種類型即淺clone和深clone。首先,分析一下兩種的不同。淺clone和深clone都是clone,它們本質區別是對象內部的成員屬性(非原生類型屬性,如int等)在clone時是否處理為引用。如果仍然保留為引用,則稱為淺clone,反之稱為深clone。其實這兩個概念也是相對的概念。在處理上它們有點區別,淺clone方式得到clone對象即可,深clone方式在得到clone對象後,還需要對引用的成員屬性進行“clone”處理。從這個層次上說,深clone並沒有什麼特別地困難,簡單講就是創建好對象,再設置一些成員屬性。關於深clone,網上的文章已經有太多,有點目不暇接的感覺,本文不再贅述,這也不是本文的重點。
本文的重點是要闡述縱深clone,亦即“N深clone”。深到什麼程度為止?本文描述的目標是一直深到你想要的程度,包括深到不能再深的程度。
/**
* @return Object 返回clone的對象
* @param obj 原對象
* @param length clone的深度
*/
public Object deepClone(Object obj, int length) {
Object result = obj;
for (int i = 0; i < fIElds.length; i++) {
//對成員進行處理
//如果已不需要再判斷成員了,那除了“容器”成員外,已經clone結束
if (length <= 1) {
if (!“容器”成員) {
continue;
}
try {
//只需clone一次了,注意遞歸方法的深度參數傳入1
clonedFIEldValue = deepClone(“容器”成員的值, 1);
} catch (Exception ex2) {
ex2.printStackTrace();
return result;
}
} else {
try {
clonedFIEldValue = deepClone(成員的值, length - 1);
} catch (Exception ex) {
ex.printStackTrace();
return result;
}
}
try {
//此處為偽代碼:將clone好的值(clonedFIEldValue)設進去
} catch (Exception ex) {
ex.printStackTrace();
return result;
}
}//for..
return result;
}