Java clone()方法來(lái)由及用法
ava語(yǔ)言的一個(gè)優(yōu)點(diǎn)就是取消了指針的概念,但也導(dǎo)致了許多程序員在編程中常常忽略了對(duì)象與引用的區(qū)別,特別是先學(xué)c、c++后學(xué)java的程序員。并且由于Java不能通過(guò)簡(jiǎn)單的賦值來(lái)解決對(duì)象復(fù)制的問(wèn)題,在開(kāi)發(fā)過(guò)程中,也常常要要應(yīng)用clone()方法來(lái)復(fù)制對(duì)象。比如函數(shù)參數(shù)類(lèi)型是自定義的類(lèi)時(shí),此時(shí)便是引用傳遞而不是值傳遞。以下是一個(gè)小例子:public class A {public String name;}public class testClone {public void changeA(A a){a.name="b";}public void changInt(int i){i=i*2+100;}/*** @param args*/public static void main(String[] args) {// TODO Auto-generated method stubtestClone test=new testClone();A a=new A();a.name="a";System.out.println("before change : a.name="+a.name);test.changeA(a);System.out.println("after change : a.name="+a.name);int i=1;System.out.println("before change : i="+i);test.changInt(i);System.out.println("after change : i="+i);}}
此時(shí)輸出的結(jié)果是:before change : a.name=aafter change : a.name=bbefore change : i=1after change : i=1
從這個(gè)例子知道Java對(duì)對(duì)象和基本的數(shù)據(jù)類(lèi)型的處理是不一樣的。在Java中用對(duì)象的作為入口參數(shù)的傳遞則缺省為"引用傳遞",也就是說(shuō)僅僅傳遞了對(duì)象的一個(gè)"引用",這個(gè)"引用"的概念同C語(yǔ)言中的指針引用是一樣的。當(dāng)函數(shù)體內(nèi)部對(duì)輸入變量改變時(shí),實(shí)質(zhì)上就是在對(duì)這個(gè)對(duì)象的直接操作。 除了在函數(shù)傳值的時(shí)候是"引用傳遞",在任何用"="向?qū)ο笞兞抠x值的時(shí)候都是"引用傳遞",如:A a1=new A();A a2=new A();a1.name="a1";a2=a1;a2.name="a2";System.out.println("a1.name="+a1.name);System.out.println("a2.name="+a2.name);
此時(shí)輸出的結(jié)果是: a1.name=a2 a2.name=a2 如果我們要用a2保存a1對(duì)象的數(shù)據(jù),但又不希望a2對(duì)象數(shù)據(jù)被改變時(shí)不影響到a1。實(shí)現(xiàn)clone()方法是其一種最簡(jiǎn)單,也是最高效的手段。 下面我們來(lái)實(shí)現(xiàn)A的clone方法public class A implements Cloneable {public String name;public Object clone() {A o = null;try {o = (A) super.clone();} catch (CloneNotSupportedException e) {e.printStackTrace();}return o;}}
首先要實(shí)現(xiàn)Cloneable接口,然后在重載clone方法,最后在clone()方法中調(diào)用了super.clone(),這也意味著無(wú)論clone類(lèi)的繼承結(jié)構(gòu)是什么樣的,super.clone()直接或間接調(diào)用了java.lang.Object類(lèi)的clone()方法。A a1=new A();A a2=new A();a1.name="a1";a2=a1;a2.name="a2";System.out.println("a1.name="+a1.name);System.out.println("a2.name="+a2.name);
此時(shí)輸出的結(jié)果是: a1.name=a1 a2.name=a2 當(dāng)Class A成員變量類(lèi)型是java的基本類(lèi)型時(shí)(外加String類(lèi)型),只要實(shí)現(xiàn)如上簡(jiǎn)單的clone(稱影子clone)就可以。但是如果Class A成員變量是數(shù)組或復(fù)雜類(lèi)型時(shí),就必須實(shí)現(xiàn)深度clone。public class A implements Cloneable {public String name[];public A(){name=new String[2];}public Object clone() {A o = null;try {o = (A) super.clone();} catch (CloneNotSupportedException e) {e.printStackTrace();}return o;}}
測(cè)試代碼A a1=new A();A a2=new A();a1.name[0]="a";a1.name[1]="1";a2=(A)a1.clone();a2.name[0]="b";a2.name[1]="1";System.out.println("a1.name="+a1.name);System.out.println("a1.name="+a1.name[0]+a1.name[1]);System.out.println("a2.name="+a2.name);System.out.println("a2.name="+a2.name[0]+a2.name[1]);
輸出結(jié)果: a1.name=[Ljava.lang.String;@757aef a1.name=b1 a2.name=[Ljava.lang.String;@757aef a2.name=b1 看到了吧,a1.name,a2.name的hash值都是@757aef,也就是說(shuō)影子clone對(duì)name數(shù)組只是clone他們的地址!解決該辦法是進(jìn)行深度clone。public Object clone() {A o = null;try {o = (A) super.clone();o.name=(String[])name.clone();//其實(shí)也很簡(jiǎn)單^_^} catch (CloneNotSupportedException e) {e.printStackTrace();}return o;}
此時(shí)輸出結(jié)果是: a1.name=[Ljava.lang.String;@757aef a1.name=a1 a2.name=[Ljava.lang.String;@d9f9c3 a2.name=b1 需要注意的是Class A存在更為復(fù)雜的成員變量時(shí),如Vector等存儲(chǔ)對(duì)象地址的容器時(shí),就必須clone徹底。public class A implements Cloneable {public String name[];public Vector<B> claB;public A(){name=new String[2];claB=new Vector<B>();}public Object clone() {A o = null;try {o = (A) super.clone();o.name==(String[])name.clone();//深度cloneo.claB=new Vector<B>();//將clone進(jìn)行到底for(int i=0;i<claB.size();i++){B temp=(B)claB.get(i).clone();//當(dāng)然Class B也要實(shí)現(xiàn)相應(yīng)clone方法o.claB.add(temp);}} catch (CloneNotSupportedException e) {e.printStackTrace();}return o;}}
public class A { public String name; } public class testClone { public void changeA(A a){ a.name="b"; } public void changInt(int i){ i=i*2+100; } /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub testClone test=new testClone(); A a=new A(); a.name="a"; System.out.println("before change : a.name="+a.name); test.changeA(a); System.out.println("after change : a.name="+a.name); int i=1; System.out.println("before change : i="+i); test.changInt(i); System.out.println("after change : i="+i); } } |
before change : a.name=a after change : a.name=b before change : i=1 after change : i=1 |
從這個(gè)例子知道Java對(duì)對(duì)象和基本的數(shù)據(jù)類(lèi)型的處理是不一樣的。在Java中用對(duì)象的作為入口參數(shù)的傳遞則缺省為"引用傳遞",也就是說(shuō)僅僅傳遞了對(duì)象的一個(gè)"引用",這個(gè)"引用"的概念同C語(yǔ)言中的指針引用是一樣的。當(dāng)函數(shù)體內(nèi)部對(duì)輸入變量改變時(shí),實(shí)質(zhì)上就是在對(duì)這個(gè)對(duì)象的直接操作。
除了在函數(shù)傳值的時(shí)候是"引用傳遞",在任何用"="向?qū)ο笞兞抠x值的時(shí)候都是"引用傳遞",如:
A a1=new A(); A a2=new A(); a1.name="a1"; a2=a1; a2.name="a2"; System.out.println("a1.name="+a1.name); System.out.println("a2.name="+a2.name); |
此時(shí)輸出的結(jié)果是:
a1.name=a2
a2.name=a2
如果我們要用a2保存a1對(duì)象的數(shù)據(jù),但又不希望a2對(duì)象數(shù)據(jù)被改變時(shí)不影響到a1。實(shí)現(xiàn)clone()方法是其一種最簡(jiǎn)單,也是最高效的手段。
下面我們來(lái)實(shí)現(xiàn)A的clone方法
public class A implements Cloneable { public String name; public Object clone() { A o = null; try { o = (A) super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return o; } } |
首先要實(shí)現(xiàn)Cloneable接口,然后在重載clone方法,最后在clone()方法中調(diào)用了super.clone(),這也意味著無(wú)論clone類(lèi)的繼承結(jié)構(gòu)是什么樣的,super.clone()直接或間接調(diào)用了java.lang.Object類(lèi)的clone()方法。
A a1=new A(); A a2=new A(); a1.name="a1"; a2=a1; a2.name="a2"; System.out.println("a1.name="+a1.name); System.out.println("a2.name="+a2.name); |
此時(shí)輸出的結(jié)果是:
a1.name=a1
a2.name=a2
當(dāng)Class A成員變量類(lèi)型是java的基本類(lèi)型時(shí)(外加String類(lèi)型),只要實(shí)現(xiàn)如上簡(jiǎn)單的clone(稱影子clone)就可以。但是如果Class A成員變量是數(shù)組或復(fù)雜類(lèi)型時(shí),就必須實(shí)現(xiàn)深度clone。
public class A implements Cloneable { public String name[]; public A(){ name=new String[2]; } public Object clone() { A o = null; try { o = (A) super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return o; } } |
測(cè)試代碼
A a1=new A(); A a2=new A(); a1.name[0]="a"; a1.name[1]="1"; a2=(A)a1.clone(); a2.name[0]="b"; a2.name[1]="1"; System.out.println("a1.name="+a1.name); System.out.println("a1.name="+a1.name[0]+a1.name[1]); System.out.println("a2.name="+a2.name); System.out.println("a2.name="+a2.name[0]+a2.name[1]); |
輸出結(jié)果:
a1.name=[Ljava.lang.String;@757aef
a1.name=b1
a2.name=[Ljava.lang.String;@757aef
a2.name=b1
看到了吧,a1.name,a2.name的hash值都是@757aef,也就是說(shuō)影子clone對(duì)name數(shù)組只是clone他們的地址!解決該辦法是進(jìn)行深度clone。
public Object clone() { A o = null; try { o = (A) super.clone(); o.name=(String[])name.clone();//其實(shí)也很簡(jiǎn)單^_^ } catch (CloneNotSupportedException e) { e.printStackTrace(); } return o; } |
此時(shí)輸出結(jié)果是:
a1.name=[Ljava.lang.String;@757aef
a1.name=a1
a2.name=[Ljava.lang.String;@d9f9c3
a2.name=b1
需要注意的是Class A存在更為復(fù)雜的成員變量時(shí),如Vector等存儲(chǔ)對(duì)象地址的容器時(shí),就必須clone徹底。
public class A implements Cloneable { public String name[]; public Vector<B> claB; public A(){ name=new String[2]; claB=new Vector<B>(); } public Object clone() { A o = null; try { o = (A) super.clone(); o.name==(String[])name.clone();//深度clone o.claB=new Vector<B>();//將clone進(jìn)行到底 for(int i=0;i<claB.size();i++){ B temp=(B)claB.get(i).clone();//當(dāng)然Class B也要實(shí)現(xiàn)相應(yīng)clone方法 o.claB.add(temp); } } catch (CloneNotSupportedException e) { e.printStackTrace(); } return o; } } |
posted on 2013-12-09 10:37 順其自然EVO 閱讀(253) 評(píng)論(0) 編輯 收藏