赞
踩
源码:https://github.com/GiraffePeng/design-patterns
原型模式(Prototype Pattern)是指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
原型模式主要适用于以下场景:
在 Spring 中,原型模式应用得非常广泛,例如 scope=“prototype”。在我们经常用的 JSON.parseObject()也是一种原型模式。
针对接口编程,让我们先创建一个克隆接口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也为浅克隆。
深克隆就是仅仅克隆一个对象的值,而不克隆其引用地址,相当于重新在堆内存中开辟一块空间分配个该对象中的属性。
实现方式我们可以采用序列化与反序列化来实现。
同样创建接口:
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
可以看到深克隆下,克隆的仅仅为值,并不会去克隆引用类型的分配地址。
如果我们克隆的目标的对象是单例对象,那意味着,深克隆就会破坏单例。实际上防止克隆破坏单例解决思路非常简单,禁止深克隆便可。要么单例类不实现Cloneable 接口;要么我们重写 clone()方法,在 clone 方法中返回单例对象即可,具体如下:
....
@Override
protected Object clone() {
return 具体的单例实例对象;
}
....
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。