原型模式
源码:https://github.com/GiraffePeng/design-patterns
1、定义
原型模式(Prototype Pattern)是指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
2、应用场景
原型模式主要适用于以下场景:
- 1、类初始化消耗资源较多。
- 2、new 产生的一个对象需要非常繁琐的过程(数据准备、访问权限等)
- 3、构造函数比较复杂。
- 4、循环体中生产大量对象时。
在 Spring 中,原型模式应用得非常广泛,例如 scope=“prototype”。在我们经常用的 JSON.parseObject()也是一种原型模式。
3、浅克隆
针对接口编程,让我们先创建一个克隆接口PrototypeInteface.java
public interface PrototypeInteface {
public PrototypeInteface clone();
}
创建一个实体类实现PrototypeInteface接口,重写clone方法。
public class ShallowPrototype implements PrototypeInteface{
private int id;
private String name;
private String type;
private List<String> arr;
public List<String> getArr() {
return arr;
}
public void setArr(List<String> arr) {
this.arr = arr;
}
public ShallowPrototype() {
super();
}
public ShallowPrototype(int id, String name, String type,List<String> arr) {
super();
this.id = id;
this.name = name;
this.type = type;
this.arr = arr;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
@Override
public ShallowPrototype clone() {
ShallowPrototype shallowPrototype = new ShallowPrototype();
shallowPrototype.setId(this.id);
shallowPrototype.setName(this.name);
shallowPrototype.setType(this.type);
shallowPrototype.setArr(this.arr);
return shallowPrototype;
}
}
创建测试类:
public class PrototypeTest {
public static void main(String[] args) {
ShallowPrototype shallowPrototype = new ShallowPrototype(1, "name", "type",new ArrayList<String>() {{
add("测试");
}});
ShallowPrototype clone = shallowPrototype.clone();
System.out.println(shallowPrototype.getName() == clone.getName());
System.out.println(shallowPrototype.getArr() == clone.getArr());
clone.getArr().add("修改");
List<String> arr = shallowPrototype.getArr();
for (String string : arr) {
System.out.println(string);
}
}
}
执行后打印的控制台结果:
true
true
测试
修改
从测试结果看出 arr 集合的引用地址是相同的,意味着复制的不是值,而是引用的地址。这样的话,如果我们修改任意一个对象中的引用数据类型,shallowPrototype 和clone 的 arr 值(List)都会改变。这就是我们常说的浅克隆。只是完整复制了值的数据,没有复制引用对象本身去创建新的引用对象。换言之,所有的引用对象仍然指向原来的对象。
JDK中也默认提供了Object的clone方法,只需在要求可以克隆的类上实现Cloneable接口,写出clone方法,调用super.clone即可实现浅克隆,例如:
//实现Cloneable接口
public class PrototypeClone implements Cloneable{
private int id;
private String name;
private String type;
private List<String> arr;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public List<String> getArr() {
return arr;
}
public void setArr(List<String> arr) {
this.arr = arr;
}
public PrototypeClone(int id, String name, String type, List<String> arr) {
super();
this.id = id;
this.name = name;
this.type = type;
this.arr = arr;
}
public PrototypeClone clone() {
try {
//调用super.clone方法
return (PrototypeClone) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
}
创建测试类:
public class TestJdkClone {
public static void main(String[] args) {
PrototypeClone prototypeClone = new PrototypeClone(1, "123", "1", new ArrayList<String>() {{add("ceshi");}});
PrototypeClone clone = prototypeClone.clone();
System.out.println(prototypeClone.getArr() == clone.getArr());
}
}
打印结果:
true
说明JDK提供的clone也为浅克隆。
4、深克隆
深克隆就是仅仅克隆一个对象的值,而不克隆其引用地址,相当于重新在堆内存中开辟一块空间分配个该对象中的属性。
实现方式我们可以采用序列化与反序列化来实现。
同样创建接口:
public interface DeepCloneInterface {
public Object cloneObject();
}
创建实现类,实现DeepCloneInterface 以及 Serializable接口(为了反序列化)
public class DeepPrototype implements DeepCloneInterface,Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
private int id;
private String name;
private List<String> arr;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<String> getArr() {
return arr;
}
public void setArr(List<String> arr) {
this.arr = arr;
}
//序列化与反序列化实现深克隆
@Override
public Object cloneObject() {
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
Object readObject = ois.readObject();
ois.close();
bis.close();
oos.flush();
oos.close();
bos.close();
return readObject;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
测试类:
public class DeepTest {
public static void main(String[] args) {
DeepPrototype deepPrototype = new DeepPrototype();
deepPrototype.setArr(new ArrayList<String>() {{add("测试");}});
deepPrototype.setId(1);
deepPrototype.setName("测试");
DeepPrototype cloneObject = (DeepPrototype)deepPrototype.cloneObject();
System.out.println(cloneObject.getArr() == deepPrototype.getArr());
}
}
打印结果:
false
可以看到深克隆下,克隆的仅仅为值,并不会去克隆引用类型的分配地址。
5、克隆破坏单例模式
如果我们克隆的目标的对象是单例对象,那意味着,深克隆就会破坏单例。实际上防止克隆破坏单例解决思路非常简单,禁止深克隆便可。要么单例类不实现Cloneable 接口;要么我们重写 clone()方法,在 clone 方法中返回单例对象即可,具体如下:
....
@Override
protected Object clone() {
return 具体的单例实例对象;
}
....