Java語言的一個優勢就是取消了指針的概念,但也致使了許多程序員在編程中經常忽略了對象與引用的區別,特別是先學c、c++後學java的程序員。而且因爲Java不能經過簡單的賦值來解決對象複製的問題,在開發過程當中,也經常要要應用clone()方法來複制對象。好比函數參數類型是自定義的類時,此時即是引用傳遞而不是值傳遞。如下是一個小例子:javascript
Java代碼
public class A { c++
public String name; 程序員
} 編程
Java代碼
public class testClone { app
public void changeA(A a){ 函數
a.name="b"; 測試
} spa
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);
}
}
此時輸出的結果是:
Java代碼
before change : a.name=a
after change : a.name=b
before change : i=1
after change : i=1
從這個例子知道Java對對象和基本的數據類型的處理是不同的。在Java中用對象的做爲入口參數的傳遞則缺省爲"引用傳遞",也就是說僅僅傳遞了對象的一個"引用",這個"引用"的概念同C語言中的指針引用是同樣的。當函數體內部對輸入變量改變時,實質上就是在對這個對象的直接操做。
除了在函數傳值的時候是"引用傳遞",在任何用"="向對象變量賦值的時候都是"引用傳遞",如:
Java代碼
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);
此時輸出的結果是:
Java代碼
a1.name=a2
a2.name=a2
若是咱們要用a2保存a1對象的數據,但又不但願a2對象數據被改變時不影響到a1。實現clone()方法是其一種最簡單,也是最高效的手段。
下面咱們來實現A的clone方法
Java代碼
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;
}
}
首先要實現Cloneable接口,而後在重載clone方法,最後在clone()方法中調用了super.clone(),這也意味着不管clone類的繼承結構是什麼樣的,super.clone()直接或間接調用了java.lang.Object類的clone()方法。
Java代碼
A a1=new A();
A a2=new A();
a1.name="a1";
a2=a1; (clone) //todo 需測試
a2.name="a2";
System.out.println("a1.name="+a1.name);
System.out.println("a2.name="+a2.name);
此時輸出的結果是:
Java代碼
a1.name=a1
a2.name=a2
當Class A成員變量類型是java的基本類型時(外加String類型),只要實現如上簡單的clone(稱影子clone)就能夠。可是若是Class A成員變量是數組或複雜類型時,就必須實現深度clone。
Java代碼
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;
}
}
測試代碼
Java代碼
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]);
輸出結果:
Java代碼
a1.name=[Ljava.lang.String;@757aef
a1.name=b1
a2.name=[Ljava.lang.String;@757aef
a2.name=b1
看到了吧,a1.name,a2.name的hash值都是@757aef,也就是說影子clone對name數組只是clone他們的地址!解決該辦法是進行深度clone。
Java代碼
public Object clone() {
A o = null;
try {
o = (A) super.clone();
o.name=(String[])name.clone();//其實也很簡單^_^
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return o;
}
此時輸出結果是:
Java代碼
a1.name=[Ljava.lang.String;@757aef
a1.name=a1
a2.name=[Ljava.lang.String;@d9f9c3
a2.name=b1
須要注意的是Class A存在更爲複雜的成員變量時,如Vector等存儲對象地址的容器時,就必須clone完全。
Java代碼
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進行到底
for(int i=0;i<claB.size();i++){
B temp=(B)claB.get(i).clone();//固然Class B也要實現相應clone方法
o.claB.add(temp);
}
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return o;
}
}