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