原型模式
用原型实例指定创建对象的,拷贝这些对象生成新的对象进行使用。
也可以直接进行new一个对象,但是当对象的构造复杂时,new的效率会很低,使用clone更好
new适用于简单的构造
clone适用于复杂的构造
应用场景
- 资源消耗大的对象
- 节省资源,比如在for循环内创建相同的对象
- 一个对象要让其他人使用,并且使用过程中要改变这个对象的某些属性,可考虑将原有对象拷贝,并且修改需要改变的属性,在进行使用
- 对象结构复杂的情况使用拷贝
浅拷贝代码实现
<font color=#0099ff size=3 face="黑体">原型对象实现Cloneable接口</font>(默认是浅拷贝)
class Prototype implements Cloneable {
public Prototype clone() {
Prototype prototype = null;
try {
prototype = (Prototype) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return prototype;
}
}
原型对象具体的内容实现
class ConcretePrototype extends Prototype {
public String name;
public ArrayList<String> list = new ArrayList<>();
public ConcretePrototype() {
System.out.println("执行了ConcretePrototype构造函数");
}
@Override
public String toString() {
return "ConcretePrototype{" +
"name='" + name + '\'' +
", list=" + list +
'}';
}
public void show() {
System.out.println("原型模式实现类");
}
}
使用方式
ConcretePrototype cp = new ConcretePrototype();
cp.name = "原始数据";
for (int i = 0; i < 10; i++) {
cp.list.add(i, String.valueOf(i));//填充数据
}
ConcretePrototype cloneConcretePrototype = (ConcretePrototype) cp.clone();//实现拷贝
cloneConcretePrototype.name = "拷贝数据";
System.out.println(cp.toString());
System.out.println(cloneConcretePrototype.toString());
显示结果:
执行了ConcretePrototype构造函数
ConcretePrototype{name='原始数据', list=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}
ConcretePrototype{name='拷贝数据', list=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}
<font color=#0099ff size=3 face="黑体">原型对象不实现Cloneable接口</font>(默认是浅拷贝)
其实就是把cloneable接口的内容手动实现一次
class ConcretePrototype2 {
public String name;
public ArrayList<String> list = new ArrayList<>();
public ConcretePrototype2() {
System.out.println("执行了ConcretePrototype2构造函数");
}
public ConcretePrototype2 clone() {
//手动new对象进行赋值
concretePrototype2 concretePrototype2 = new ConcretePrototype2();
concretePrototype2.name = this.name;
concretePrototype2.list = this.list;
return concretePrototype2;
}
@Override
public String toString() {
return "ConcretePrototype2{" +
"name='" + name + '\'' +
", list=" + list +
'}';
}
public void show() {
System.out.println("原型模式实现类");
}
}
调用方式
ConcretePrototype2 cp = new ConcretePrototype2();
cp.name = "原始数据";
for (int i = 0; i < 10; i++) {
cp.list.add(i, String.valueOf(i));//填充数据
}
ConcretePrototype2 cloneConcretePrototype = (ConcretePrototype2) cp.clone();//实现拷贝
cloneConcretePrototype.name = "拷贝数据";
System.out.println(cp.toString());
System.out.println(cloneConcretePrototype.toString());
显示结果
执行了ConcretePrototype2构造函数
执行了ConcretePrototype2构造函数
ConcretePrototype2{name='原始数据', list=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}
ConcretePrototype2{name='拷贝数据', list=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}
构造函数执行了两次,说明了cloneable接口的复制是不会执行2次构造的,直接new对象则会调用2次构造。
测试
修改复制后的数据
ConcretePrototype2 cp = new ConcretePrototype2();
cp.name = "原始数据";
for (int i = 0; i < 10; i++) {
cp.list.add(i, String.valueOf(i));//填充数据
}
ConcretePrototype2 cloneConcretePrototype = (ConcretePrototype2) cp.clone();//实现拷贝
cloneConcretePrototype.name = "拷贝数据";
cloneConcretePrototype.list.add("追加测试数据");
System.out.println(cp.toString());
System.out.println(cloneConcretePrototype.toString());
显示结果
执行了ConcretePrototype2构造函数
执行了ConcretePrototype2构造函数
ConcretePrototype2{name='原始数据', list=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 追加测试数据]}
ConcretePrototype2{name='拷贝数据', list=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 追加测试数据]}
【追加测试数据无论是原型还是拷贝后的对象都有这个数据】
问题原因:深拷贝-浅拷贝
Cloneable接口默认实现的是浅拷贝,对于基本类型会进行拷贝,但是对于引用类型(集合,数据,对象)等clone仅仅代表指向了同一个内存地址,所以修改一个两个都会变化。
解决方案:在引用类型的具体数据类型在进行一次clone,将浅拷贝处理为深拷贝
深拷贝代码实现
代码如下
public ConcretePrototype2 clone() {
//手动new对象进行赋值
// ConcretePrototype2 concretePrototype2 = new ConcretePrototype2();
// concretePrototype2.name = this.name;
// concretePrototype2.list = this.list;
// return concretePrototype2;
//将具体的集合修改为深拷贝
ConcretePrototype2 concretePrototype2 = new ConcretePrototype2();
concretePrototype2.name = this.name;
concretePrototype2.list = (ArrayList<String>) this.list.clone();
return concretePrototype2;
}
显示结果
执行了ConcretePrototype2构造函数
执行了ConcretePrototype2构造函数
ConcretePrototype2{name='原始数据', list=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}
ConcretePrototype2{name='拷贝数据', list=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 追加测试数据]}
应证我们的想法,对于集合,数组,对象等,如果要新增数据,需要对其重写更改具体的数据类型。
总结
原型模式可以避免构造复杂的对象时的资源消耗问题,提升创建对象的效率。
可以保护原始对象的数据安全性
clone是二进制流,对于复杂的构造对象,性能提升明显,但是对于简单的构造没必要使用clone