前几天在项目中碰到一个bug。
场景:
页面中点击购买按钮出现购买商品的颜色尺码窗口的dialog,这个dialog需要商品详情传递商品原始数据过去(暂且叫对象A,通过Bundle传递),dialog里面接收到的叫对象B。当我在dialog里某个地方修改对象B的属性值后,当我再返回商品详情时,商品详情的对象A对应的属性值也已经被修改。
猜测:
通过bundle传递的对象和在dialog里面接收的对象都是指向同一个地址,目前尚未真正弄清楚原因,有知道的小伙伴在看完文章后请留言我哦,一起讨论学习。
解决办法:
既然这样,就需要考虑重新拷贝一份对象A再传递给dialog了。刚好前几天在复习设计模式的时候看到了原型模式,正好派上用场。先看示例:
import java.util.ArrayList;
/**
* 作者:William 时间:2017/12/4
*/
public class WilliamData implements Cloneable {
// 标题
private String title;
// 图片名列表
private ArrayList<String> images = new ArrayList<>();
public WilliamData() {
System.out.println("------------WilliamData 构造函数-----------");
}
@Override
protected WilliamData clone() {
try {
WilliamData doc = (WilliamData) super.clone();
doc.title = this.title;
// 浅拷贝(影子拷贝)
// doc.images = this.images;
// 深拷贝,对images对象也调用clone()函数,进行深拷贝
//noinspection unchecked
doc.images = (ArrayList<String>) this.images.clone();
return doc;
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
public void setTitle(String title) {
this.title = title;
}
public void addImage(String image) {
this.images.add(image);
}
// 打印数据内容
public void showData() {
System.out.println("-------- Start ---------");
System.out.println("title : " + title);
System.out.println("ImagesList : ");
for (String imgName : images) {
System.out.println("image name : " + imgName);
}
System.out.println("-------- End ---------");
}
}
测试类:
/**
* 作者:William 时间:2017/12/4
* 类说明:原型模式测试类————创建型模式
* 1.原型模式的定义:
* <p>
* 用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。
* </p>
* 2.原型模式的使用场景
* <p>
* (1) 类初始化需要消耗非常多的资源,这个资源包括数据、硬件资源等,通过原型拷贝避免这些消耗。
* (2) 通过new产生一个对象需要非常繁琐的数据准备或访问权限,这时可以使用原型模式。
* (3) 一个对象需要提供给其他对象访问,而且各个调用者都有可能需要修改其中属性值时,使用保护性拷贝。
* </p>
* 3.优点:
* <p>
* 原型模式是在内存中二进制流的拷贝,要比直接new一个对象性能好很多,特别是在一个循环体内产生大量对象时,原型模式可以更好的体现其优点
* </p>
* 4.缺点:
* <p>
* 这既是其优点也是缺点,直接在内存中拷贝,构造函数是不会执行的。
* </p>
*/
public class TestPrototype {
public static void main(String[] args) {
// 1.构建对象
WilliamData originData = new WilliamData();
// 2.编辑数据,添加图片
originData.setTitle("这是一份数据");
originData.addImage("图片 1");
originData.addImage("图片 2");
originData.addImage("图片 3");
originData.showData();
// 以原始数据为原型,拷贝一份副本
WilliamData copyData = originData.clone();
copyData.showData();
// 修改副本,不会影响原始数据
copyData.setTitle("这是修改过的数据");
copyData.addImage("new_image.jpg");
copyData.showData();
// 打印原始数据
originData.showData();
}
}
运行结果:
------------WilliamData 构造函数-----------
-------- Start ---------
title : 这是一份数据
ImagesList :
image name : 图片 1
image name : 图片 2
image name : 图片 3
-------- End ---------
-------- Start ---------
title : 这是一份数据
ImagesList :
image name : 图片 1
image name : 图片 2
image name : 图片 3
-------- End ---------
-------- Start ---------
title : 这是修改过的数据
ImagesList :
image name : 图片 1
image name : 图片 2
image name : 图片 3
image name : new_image.jpg
-------- End ---------
-------- Start ---------
title : 这是一份数据
ImagesList :
image name : 图片 1
image name : 图片 2
image name : 图片 3
-------- End ---------
通过运行测试类就可以发现通过拷贝后的对象修改属性值后不会影响原始对象的值:
实现原型模式也很简单,只需要实现Cloneable的clone方法,同时需要注意深拷贝和浅拷贝的问题。
还有通过拷贝构造函数方式来实现原型模式,在此不展开了。
至此,问题解决。