深入浅出Java clone技术
生活随笔
收集整理的這篇文章主要介紹了
深入浅出Java clone技术
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
這是clone技術介紹的第一篇。本篇主要介紹對象clone技術的基本知識。?
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”。深到什么程度為止?本文描述的目標是一直深到你想要的程度,包括深到不能再深的程度。?
實現方案為采用java reflection技術和遞歸相結合。?
大致步驟描述如下:首先采用java reflection技術動態獲取成員方法列表。然后視clone的深度,對具備clone條件的并且有必要clone的成員進行逐一clone。這是一個遞歸的過程,直到clolne深度已到為止或者到對象已經沒有需要clone的成員屬性為止。?
何為具備clone條件的并且有必要clone的成員進行逐一clone?比如,原生類型(primitive type),定為瞬態(Transient)的類型,不可訪問的類型(!Field#isAccessible()),沒實現Cloneable接口的類型等等,都不具備clone條件。String等java定義的類型就不需要再深入clone了,這些屬于沒必要進行clone的情況。但List類型等“容器”類是有必要clone的成員類型。?
據此,遞歸程序示意如下(deepClone為java 方法):?
/**?
* @return Object 返回clone的對象?
* @param obj 原對象?
* @param length clone的深度?
*/?
public Object deepClone(Object obj, int length) {?
Object result = obj;?
//此處為偽代碼: 如果對象obj不具備clone條件,就返回result,這也是遞歸的結束條件。?
//此處為偽代碼: 如果對象obj沒必要clone,就返回result?
//此處為偽代碼:開始進行“clone”對象。這地方是調一個抽象方法來處理,這樣可以增加很多靈活性。該方法主要目的是實現“clone”對象方案。注意:這里面的“clone”方案可能是我們想都想不到的方案,它可能有很多創意,但效果都是一樣的,就是要“clone”個新的對象出來。當然最容易想的就是Object#clone()方法了。示意如下:?
result = om.clone(obj);?
//此處為偽代碼: 獲取具備clone條件的并且有必要clone的所有成員。這地方也是調一個抽象方法來處理。同樣是為了增強靈活性。獲取這些成員的方法有很多,可能是通過setter和getter對來得到,也可能是通過get fields 等等方法得到(這種方法可能不少成員是無法直接訪問的,往往需要結合別的方法),甚至是多種方法的綜合。總之,目的只有一個,就是獲得這些成員。?
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;?
}?
至此,已完成了“N深clone”。下面討論一下別的相關問題。比如說這種深度clone原本是A-->B-->C--……-->xz這樣一種情況,就是說A類含有B成員,B里面又含有C成員,依此類推。如果想在“N深clone”時,只clone“xz”這個成員怎么辦?其實很簡單,這個問題主要是要解決在遞歸過程中有些成員需要clone同時有些成員不需clone仍保留引用這個問題。在上面的遞歸示例中已經提到,實現“clone”的“方案”已經被定義成抽象方法,那么我們只要對這個方法做一個滿足這個需求的實現即可。?
文章出處:飛諾網(www.diybl.com):http://www.diybl.com/course/3_program/java/javashl/200726/10344.html
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”。深到什么程度為止?本文描述的目標是一直深到你想要的程度,包括深到不能再深的程度。?
實現方案為采用java reflection技術和遞歸相結合。?
大致步驟描述如下:首先采用java reflection技術動態獲取成員方法列表。然后視clone的深度,對具備clone條件的并且有必要clone的成員進行逐一clone。這是一個遞歸的過程,直到clolne深度已到為止或者到對象已經沒有需要clone的成員屬性為止。?
何為具備clone條件的并且有必要clone的成員進行逐一clone?比如,原生類型(primitive type),定為瞬態(Transient)的類型,不可訪問的類型(!Field#isAccessible()),沒實現Cloneable接口的類型等等,都不具備clone條件。String等java定義的類型就不需要再深入clone了,這些屬于沒必要進行clone的情況。但List類型等“容器”類是有必要clone的成員類型。?
據此,遞歸程序示意如下(deepClone為java 方法):?
/**?
* @return Object 返回clone的對象?
* @param obj 原對象?
* @param length clone的深度?
*/?
public Object deepClone(Object obj, int length) {?
Object result = obj;?
//此處為偽代碼: 如果對象obj不具備clone條件,就返回result,這也是遞歸的結束條件。?
//此處為偽代碼: 如果對象obj沒必要clone,就返回result?
//此處為偽代碼:開始進行“clone”對象。這地方是調一個抽象方法來處理,這樣可以增加很多靈活性。該方法主要目的是實現“clone”對象方案。注意:這里面的“clone”方案可能是我們想都想不到的方案,它可能有很多創意,但效果都是一樣的,就是要“clone”個新的對象出來。當然最容易想的就是Object#clone()方法了。示意如下:?
result = om.clone(obj);?
//此處為偽代碼: 獲取具備clone條件的并且有必要clone的所有成員。這地方也是調一個抽象方法來處理。同樣是為了增強靈活性。獲取這些成員的方法有很多,可能是通過setter和getter對來得到,也可能是通過get fields 等等方法得到(這種方法可能不少成員是無法直接訪問的,往往需要結合別的方法),甚至是多種方法的綜合。總之,目的只有一個,就是獲得這些成員。?
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;?
}?
至此,已完成了“N深clone”。下面討論一下別的相關問題。比如說這種深度clone原本是A-->B-->C--……-->xz這樣一種情況,就是說A類含有B成員,B里面又含有C成員,依此類推。如果想在“N深clone”時,只clone“xz”這個成員怎么辦?其實很簡單,這個問題主要是要解決在遞歸過程中有些成員需要clone同時有些成員不需clone仍保留引用這個問題。在上面的遞歸示例中已經提到,實現“clone”的“方案”已經被定義成抽象方法,那么我們只要對這個方法做一個滿足這個需求的實現即可。?
文章出處:飛諾網(www.diybl.com):http://www.diybl.com/course/3_program/java/javashl/200726/10344.html
總結
以上是生活随笔為你收集整理的深入浅出Java clone技术的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【网络工程师】<软考中级>网络互联与互联
- 下一篇: 詹姆斯高斯林_詹姆斯·高斯林(James