讀古今文學網 > 編寫高質量代碼:改善Java程序的151個建議 > 建議62:警惕數組的淺拷貝 >

建議62:警惕數組的淺拷貝

有這樣一個例子,第一個箱子裡有赤橙黃綠青藍紫7色氣球,現在希望在第二個箱子中也放入7個氣球,其中最後一個氣球改為藍色,也就是赤橙黃綠青藍藍7個氣球,那我們很容易就會想到第二個箱子中的氣球可以通過拷貝第一個箱子中的氣球來實現,畢竟有6個氣球是一樣的嘛,來看實現代碼:


public class Client{

public static void main(Stringargs){

//氣球數量

int ballonNum=7;

//第一個箱子

Balloonbox1=new Balloon[ballonNum];

//初始化第一個箱子中的氣球

for(int i=0;i<ballonNum;i++){

box1[i]=new Balloon(Color.values()[i],i);

}

//第二個箱子的氣球是拷貝的第一個箱子裡的

Balloonbox2=Arrays.copyOf(box1,box1.length);

//修改最後一個氣球顏色

box2[6].setColor(Color.Blue);

//打印出第一個箱子中的氣球顏色

for(Balloon b:box1){

System.out.println(b);

}

}

}

//氣球顏色

enum Color{

Red, Orange, Yellow, Green, Indigo, Blue, Violet;

}

//氣球

class Balloon{

//編號

private int id;

//顏色

private Color color;

public Balloon(Color_color, int_id){

color=_color;

id=_id;

}

/*id、color的getter/setter方法省略*/

//apache-common包下的ToStringBuilder重寫toString方法

public String toString(){

return new ToStringBuilder(this)

.append("編號",id)

.append("顏色",color)

.toString();

}

}


第二個箱子裡最後一個氣球的顏色毫無疑問是被修改成藍色了,不過我們是通過拷貝第一個箱子裡的氣球然後再修改的方式來實現的,那會對第一個箱子的氣球顏色有影響嗎?我們看輸出:


Balloon@b2fd8f[編號=0,顏色=Red]

Balloon@a20892[編號=1,顏色=Orange]

Balloon@158b649[編號=2,顏色=Yellow]

Balloon@1037c71[編號=3,顏色=Green]

Balloon@1546e25[編號=4,顏色=Indigo]

Balloon@8a0d5d[編號=5,顏色=Blue]

Balloon@a470b8[編號=6,顏色=Blue]


最後一個氣球顏色竟然也被修改了,我們只是希望修改第二個箱子的氣球啊,這是為何?這是很典型的淺拷貝(Shallow Clone)問題,前面第1章的序列化中也介紹過,但是這裡與之有一點不同:數組中的元素沒有實現Serializable接口。

確實如此,通過copyOf方法產生的數組是一個淺拷貝,這與序列化的淺拷貝完全相同:基本類型是直接拷貝值,其他都是拷貝引用地址。需要說明的是,數組的clone方法也是與此相同的,同樣是淺拷貝,而且集合的clone方法也都是淺拷貝,這就需要大家在拷貝時多留心了。

問題找到了,解決方案也很簡單,遍歷box1的每個元素,重新生成一個氣球(Ballon)對象,並放置到box2數組中,代碼較簡單,不再贅述。

該方法用得最多的地方是在使用集合(如List)進行業務處理時,比如發覺需要拷貝集合中的元素,可集合沒有提供拷貝方法,如果自己寫會很麻煩,所以乾脆使用List.toArray方法轉換成數組,然後通過Arrays.copyOf拷貝,再轉換回集合,簡單便捷!但是,非常遺憾的是,這裡我們又撞到淺拷貝的槍口上了,雖然很多時候淺拷貝可以解決業務問題,但更多時候會留下隱患,需要我們提防又提防。