首页 » 编写高质量代码:改善Java程序的151个建议 » 编写高质量代码:改善Java程序的151个建议全文在线阅读

《编写高质量代码:改善Java程序的151个建议》建议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();

}

}


第二个箱子里最后一个气球的颜色毫无疑问是被修改成蓝色了,不过我们是通过拷贝第一个箱子里的气球然后再修改的方式来实现的,那会对第一个箱子的气球颜色有影响吗?我们看输出:


[email protected][编号=0,颜色=Red]

[email protected][编号=1,颜色=Orange]

[email protected][编号=2,颜色=Yellow]

[email protected][编号=3,颜色=Green]

[email protected][编号=4,颜色=Indigo]

[email protected][编号=5,颜色=Blue]

[email protected][编号=6,颜色=Blue]


最后一个气球颜色竟然也被修改了,我们只是希望修改第二个箱子的气球啊,这是为何?这是很典型的浅拷贝(Shallow Clone)问题,前面第1章的序列化中也介绍过,但是这里与之有一点不同:数组中的元素没有实现Serializable接口。

确实如此,通过copyOf方法产生的数组是一个浅拷贝,这与序列化的浅拷贝完全相同:基本类型是直接拷贝值,其他都是拷贝引用地址。需要说明的是,数组的clone方法也是与此相同的,同样是浅拷贝,而且集合的clone方法也都是浅拷贝,这就需要大家在拷贝时多留心了。

问题找到了,解决方案也很简单,遍历box1的每个元素,重新生成一个气球(Ballon)对象,并放置到box2数组中,代码较简单,不再赘述。

该方法用得最多的地方是在使用集合(如List)进行业务处理时,比如发觉需要拷贝集合中的元素,可集合没有提供拷贝方法,如果自己写会很麻烦,所以干脆使用List.toArray方法转换成数组,然后通过Arrays.copyOf拷贝,再转换回集合,简单便捷!但是,非常遗憾的是,这里我们又撞到浅拷贝的枪口上了,虽然很多时候浅拷贝可以解决业务问题,但更多时候会留下隐患,需要我们提防又提防。