名词解释
Prototype Pattern
是指原型实例指定创建对象的种类,并且通过拷贝这些原型的属性和值来创建新的对象。
应用场景
- 类初始化消耗资源较多。
- new 产生的一个对象需要非常繁琐的过程(比如数据准备、访问权限等)
- 构造函数比较复杂。
- 循环体中生产大量对象时。
写法
简单写法
常规的简单写法是先定义一个接口
public interface Prototype{
Prototype clone();
}
然后让需要克隆的类实现这个接口,并在 clone 方法中实现对象的创建和给属性赋值,比如:
public class ConcretePrototype implements Prototype {
String attributeA;
List<String> attributeC;
@Override
public Prototype clone() {
ConcretePrototype cloneObj = new ConcretePrototype();
cloneObj.attributeA = this.attributeA;
cloneObj.attributeC = this.attributeC;
return cloneObj;
}
public String getAttributeA() {
return attributeA;
}
public void setAttributeA(String attributeA) {
this.attributeA = attributeA;
}
public List<String> getAttributeC() {
return attributeC;
}
public void setAttributeC(List<String> attributeC) {
this.attributeC = attributeC;
}
}
存在的问题
在上面实现类的代码中
List<String> attributeC;
...
cloneObj.attributeC = this.attributeC;
当对引用数据类型进行赋值时,不能直接用 = 来赋值,这样会使得被克隆出来的对象和原对象中该属性对应用到同一个地址,修改其中一个的值,另一个的只也修改了,解决办法是改用深克隆
深克隆
只通过实现 Cloneable 接口
如果存在自定义的引用数据类型,则在每一个引用的类上也要实现 Cloneable 接口,且每一次调用 clone 方法时都要注意检查对引用类型的深克隆处理
比如:Wheel类
public class Wheel implements Cloneable {
private int size ;
public Wheel(int size) {
this.size = size;
}
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
Car类会调用 Wheel类
public class Car implements Cloneable{
private String name ;
private Wheel wheel ;
public Car(String name, Wheel wheel) {
this.name = name;
this.wheel = wheel;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Wheel getWheel() {
return wheel;
public void setWheel(Wheel wheel) {
this.wheel = wheel;
}
@Override
protected Object clone() throws CloneNotSupportedException {
Car c = (Car)super.clone();
c.setWheel((Wheel)c.getWheel().clone()); //重点是这里,调用依赖类的地方也要进行克隆处理
return c;
}
}
只通过实现 Serializable 接口
同样,如果存在自定义的引用数据类型,则在每一个引用的类上也要实现 Serializable 接口,不同的是,被引用的类不用单独处理 clone 方法,只需要在最终调用的类那里通过 反序列化 来实现深克隆就可以
代码如下:Wheel2 类
public class Wheel2 implements Serializable {
private int size ;
public Wheel2(int size) {
this.size = size;
}
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
}
Car2 类来调用 Wheel2 类
public class Car2 implements Serializable {
private String name ;
private Wheel2 wheel2 ;
public Car2(String name, Wheel2 wheel2) {
this.name = name;
this.wheel2 = wheel2;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Wheel2 getWheel2() {
return wheel2;
}
public void setWheel2(Wheel2 wheel2) {
this.wheel2 = wheel2;
}
@Override
protected Object clone() throws CloneNotSupportedException {
try{
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
Car2 copy = (Car2)ois.readObject();
return copy;
}catch (Exception e){
e.printStackTrace();
return null;
}
}
}
同时实现Cloneable ,Serializable 接口
让需要克隆的类实现 Cloneable
接口和Serializable
接口,
代码如下,注意clone方法的实现
public class DeepClonePrototype implements Prototype,Cloneable, Serializable {
String attributeA;
List<String> attributeC;
@Override
public Prototype clone() {
try{
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
DeepClonePrototype copy = (DeepClonePrototype)ois.readObject();
return copy;
}catch (Exception e){
e.printStackTrace();
return null;
}
}
public String getAttributeA() {
return attributeA;
}
public void setAttributeA(String attributeA) {
this.attributeA = attributeA;
}
public List<String> getAttributeC() {
return attributeC;
}
public void setAttributeC(List<String> attributeC) {
this.attributeC = attributeC;
}
}
知识点
因为 List 类的实现类,如 ArrayList 也是实现了
Cloneable
和Serializable
接口的,所以其他类来可以对包含 List 的实例的对象进行深克隆
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
存在的问题
会对单例造成破坏,不过解决起来很简单,以下的任意一种思路都是可行的
- 单例类不实现 Cloneable 接口
- 在单例类中重写clone()方法,在clone 方法中返回单例对象
代码很简单,就不贴了。
Spring中的应用
spring 中的 scope =“prototype” 和 scope =“singleton” 用来表示对象创建的方式, prototype 表示通过已有对象来进行克隆,具体表现为每一个线程或者请求都会单独创建一次对象,以保证线程间数据完全隔离, singleton表示只创建单例,各线程得到的是同一个实例,这有利于数据共享。
所以,到底是配置prototype
还是 singleton
要看具体场景