Java克隆(Clone)的應用
簡介:
Java克隆(Clone)是Java語言的特性之一,但在實際中應用比較少見。但有時候用克隆會更方便更有效率。
對於克隆(Clone),Java有一些限制:
一、被克隆的類必須本身實現Cloneable 接口,以指示 Object.clone() 方法能夠合法地對該類實例進行按字段複製。Cloneable 接口其實是個標識接口,沒有任何接口方法。
二、實現Cloneable接口的類應該使用公共方法重寫 Object.clone(它是受保護的)。某個對象實現了此接口就克隆它是不可能的。即便 clone 方法是反射性調用的,也沒法保證它將得到成功。
三、在Java.lang.Object類中克隆方法是這麼定義的:
protected Object clone()
throws CloneNotSupportedException
建立並返回此對象的一個副本。代表是一個受保護的方法,同一個包中可見。
按照慣例,返回的對象應該經過調用 super.clone 得到。
引題:
舉個例子說吧,如今有一個對象好比叫foo,你須要在建立當前對象的一個副本做爲存根你能怎麼作?
假如你不用Clone,那麼你能夠先new一個對象foo1:Foo foo1=new Foo(),而後用foo給foo1對象set值,這樣就獲得foo的副本foo1;除此以外,別無選擇。
這樣說,也許有人會以爲說的過於絕對了,不過事實如此啊。
要產生一個副本,那副本要不要內存?----固然要了,那就對了!既然須要內存,(不克隆的狀況下)你不new還有什麼辦法呢?請你們時刻銘記
對象是Java運行時產生的,駐留在計算機內存中。
常見錯誤:
下面我澄清幾個初學者容易犯迷糊的錯誤,一樣的問題,產生foo對象的副本:
一、Foo foo1=new Foo();
foo1=foo;
而後就想固然的認爲副本foo1生成了!
錯誤緣由:foo1沒錯是申請了內存,可是執行foo1=foo後,foo1就不在指向剛申請的內存區域了,轉而指向foo對象的內存區域,這時候,foo一、foo指向了同一內存區域。剛纔new的操做製造一堆垃圾等着JVM回收。
二、Foo foo1=foo;
錯誤緣由:仍是兩個變量都指向了同一塊內存。
三、有些老鳥更厲害一些:在Foo中定義一個返回自身的方法:
public Foo getInstance(){
return this;
}
而後,Foo foo1=foo.getInstance();
錯誤緣由:同上,主要仍是沒有從新開闢內存,this在對象裏是什麼?----就是對象本身的引用!那麼getInstance()天然返回的就是對象本身,反正又是兩個對象穿了一條褲子----***,哈哈。錯得心服口服吧。爲了節省篇幅,我在最後寫個例子,留給那些對此有異議的人看。
引入克隆
看了這麼多方法都不行,還很麻煩!乾脆用克隆吧,簡單明瞭。
廢話不說了,看例子:
定義兩個類CloneFooA、CloneFooB,而後寫個測試類CloneDemo分別克隆這兩個類的對象,而後打印測試結果到控制檯。
/**
* Created by IntelliJ IDEA.
* User: leizhimin
* Date: 2007-9-20
* Time: 19:40:44
* 簡單類克隆實現
* 要實現克隆,必須實現Cloneable接口,這是一個標識接口,沒有接口方法
* 實現了 Cloneable 接口,以指示 Object.clone() 方法能夠合法地對該類實例進行按字段複製。
* 按照慣例,實現此接口的類應該使用公共方法重寫 Object.clone(它是受保護的)。
*/
public class CloneFooA
implements Cloneable {
private String strA;
private int intA;
public CloneFooA(String strA, int intA) {
this.strA = strA;
this.intA = intA;
}
public String getStrA() {
return strA;
}
public void setStrA(String strA) {
this.strA = strA;
}
public int getIntA() {
return intA;
}
public void setIntA(int intA) {
this.intA = intA;
}
/**
* @return 建立並返回此對象的一個副本。
* @throws CloneNotSupportedException
*/
public Object
clone() throws
CloneNotSupportedException {
//直接調用父類的clone()方法,返回克隆副本
return super.clone();
}
}
/**
* Created by IntelliJ IDEA.
* User: leizhimin
* Date: 2007-9-20
* Time: 19:59:55
* 深度克隆對象,當類存在聚合關係的時候,克隆就必須考慮聚合對象的克隆
*/
public class CloneFooB
implements Cloneable {
private
CloneFooA fooA;
private Double douB;
public CloneFooB(Double douB) {
this.douB = douB;
}
public CloneFooB(CloneFooA fooA, Double douB) {
this.fooA = fooA;
this.douB = douB;
}
public CloneFooA getFooA() {
return fooA;
}
public void setFooA(CloneFooA fooA) {
this.fooA = fooA;
}
public Double getDouB() {
return douB;
}
public void setDouB(Double douB) {
this.douB = douB;
}
/**
* 克隆操做
*
* @return 自身對象的一個副本
* @throws CloneNotSupportedException
*/
public Object clone() throws CloneNotSupportedException {
//先調用父類的克隆方法進行克隆操做
CloneFooB cloneFooB = (CloneFooB) super.clone();
//對於克隆後出的對象cloneFooB,若是其成員fooA爲null,則不能調用clone(),不然出空指針異常
if (this.fooA != null)
cloneFooB.fooA = (CloneFooA) this.fooA.clone();
return cloneFooB;
}
}
/**
* Created by IntelliJ IDEA.
* User: leizhimin
* Date: 2007-9-20
* Time: 19:52:01
* 測試類:分別克隆CloneFooA和CloneFooB類,並打印克隆先後的結果.
*/
public class CloneDemo {
public static void main(String args[]) throws CloneNotSupportedException {
//CloneFooA克隆前
CloneFooA fooA1 = new CloneFooA("FooA", 11);
System.out.println("CloneFooA的對象克隆前對象fooA1值爲: " + fooA1.getStrA() + "," + fooA1.getIntA());
//CloneFooA克隆後
CloneFooA fooA2 = (CloneFooA) fooA1.clone();
System.out.println("CloneFooA的對象克隆後對象fooA2值爲: " + fooA2.getStrA() + "," + fooA2.getIntA());
//比較fooA1和fooA2內存地址
if (fooA1 == fooA2) System.out.println("比較fooA1和fooA2內存地址:相等!");
else System.out.println("比較fooA1和fooA2內存地址:不相等!");
System.out.println("-------------------------");
//CloneFooB克隆前
CloneFooB fooB1 = new CloneFooB(fooA1, new Double("33"));
System.out.println("CloneFooB的對象克隆前對象fooB1值爲: " + fooB1.getFooA().getStrA() + "," + fooB1.getFooA().getIntA() + " | " + fooB1.getDouB());
//CloneFooB克隆後
CloneFooB fooB2 = (CloneFooB) fooB1.clone();
System.out.println("CloneFooB的對象克隆前對象fooB2值爲: " + fooB2.getFooA().getStrA() + "," + fooB2.getFooA().getIntA() + " | " + fooB2.getDouB());
if (fooA1 == fooA2) System.out.println("比較fooB1和fooB2內存地址:相等!");
else System.out.println("比較fooB1和fooB2內存地址:不相等!");
}
}
運行結果:
CloneFooA的對象克隆前對象fooA1值爲: FooA,11
CloneFooA的對象克隆後對象fooA2值爲: FooA,11
比較fooA1和fooA2內存地址:不相等!
-------------------------
CloneFooB的對象克隆前對象fooB1值爲: FooA,11 | 33.0
CloneFooB的對象克隆前對象fooB2值爲: FooA,11 | 33.0
比較fooB1和fooB2內存地址:不相等!
Process finished with exit code 0
反面教材:
最後,我給出我上面提出到最後要給出的反面例子。
隨便寫一個,在CloneFooA 的基礎上作了少量改動,內容以下:
public class CloneFooA implements Cloneable {
private String strA;
private int intA;
public CloneFooA(String strA, int intA) {
this.strA = strA;
this.intA = intA;
}
public String getStrA() {
return strA;
}
public void setStrA(String strA) {
this.strA = strA;
}
public int getIntA() {
return intA;
}
public void setIntA(int intA) {
this.intA = intA;
}
/**
* @return 建立並返回此對象的一個副本。
* @throws CloneNotSupportedException
*/
public Object clone() throws CloneNotSupportedException {
//直接調用父類的clone()方法,返回克隆副本
return super.clone();
}
/**
* @return 返回運行時的對象
*/
public CloneFooA getInstance(){
return this;
}
public static void main(String args[]){
CloneFooA fooA=new CloneFooA("aa",11);
System.out.println(fooA.getStrA()+" "+fooA.getIntA());
CloneFooA fooA1=fooA.getInstance();
System.out.println(fooA1.getStrA()+" "+fooA1.getIntA());
if(fooA==fooA1) System.out.println("fooA和fooA1內存地址相等!");
System.out.println("-------------------------");
//改變後fooA或者fooA1中任何一個,看看另一個是否會改變
fooA1.setStrA("bb");
System.out.println(fooA.getStrA()+" "+fooA.getIntA());
System.out.println(fooA1.getStrA()+" "+fooA1.getIntA());
if(fooA==fooA1) System.out.println("fooA和fooA1內存地址相等,改變fooA1後,fooA的值也跟着變化了");
}
}
運行結果:
aa 11 aa 11 fooA和fooA1內存地址相等! ------------------------- bb 11 bb 11 fooA和fooA1內存地址相等,改變fooA1後,fooA的值也跟着變化了