什么是原型模式
原型模式是指使用原型实例指定创建对象的类型,并通过拷贝这些原型创建新的对象。
原型模式的原理是将一个原型对象传给要创建的对象,这个要创建的对象通过请求原型对象拷贝自己来实现创建过程。
原型模式包含以下几个角色:
Prototype(抽象原型):声明克隆方法的接口,是所有具体类型的公共父类,可以是抽象类也可以是接口,甚至可以是具体实现类。
ConcretePrototype(具体原型):实现在抽象原型类中什么的克隆方法,在克隆方法中返回自己的一个新的克隆对象。
Client(客户端):让一个原型对象克隆自身从而创建一个新的对象,在客户端只需要直接实例化或通过工厂方法等方式创建一个原型对象,再通过调用该对象的克隆方法就得到多个相同的对象。
原型模式中有两种克隆方法,深拷贝和浅拷贝,区别在于是否支持引用类型的成员变量的复制。
浅拷贝:如果原型对象的成员变量是值类型,则复制一份给克隆对象;如果原型对象的成员变量是引用类型,则将引用对象的地址复制一份给克隆对象,也就是说原型对象和克隆对象的成员变量指向相同的内存地址。也就是说浅拷贝时,对象被复制时只会复制对象本身和其中包含的值类型的成员变量,而引用类型的成员变量并不会去复制。
深拷贝:无论原型对象的成员变量是值类型还是引用类型,都将复制一份给克隆对象。深拷贝将原型对象的所有引用对象也一起复制一份给克隆对象。
原型模式的优缺点
优点
- 当创建一个对象实例较为复杂时,使用原型模式可以简化创建过程。
- 提供了抽象原型类,扩展性较好。
- 可以使用深拷贝的方式复制一个临时对象。
缺点
- 需要为每一个类内部都声明一个克隆方法,当需要对拷贝方法修改时需要修改源码,违背开闭原则。
- 实现深拷贝时实现比较复杂,当对象之间存在多重嵌套时要实现深拷贝就需要对每一层对象类支持深拷贝。
原型模式的适用场景
- 创建的新对象比较复杂。
- 如果需要临时保存对象的状态,而对象的状态变化比较小。
- 要实例化的类在运行时指定时。
原型模式案例
// 1. 浅拷贝
@Data
public class Sheep implements Cloneable {
private String name;
private int age;
private String color;
private String address = "喜羊羊";
public Sheep friend;
public Sheep(String name, int age, String color) {
super();
this.name = name;
this.age = age;
this.color = color;
}
@Override
public String toString() {
return "Sheep [name=" + name + ", age=" + age + ", color=" + color + ", address=" + address + "]";
}
@Override
protected Object clone() {
Sheep sheep = null;
try {
sheep = (Sheep) super.clone();
} catch (Exception e) {
System.out.println(e.getMessage());
}
return sheep;
}
}
public static void main(String[] args) {
Sheep sheep = new Sheep("tom", 1, "白色");
sheep.friend = new Sheep("jack", 2, "黑色");
Sheep sheep2 = (Sheep)sheep.clone();
Sheep sheep3 = (Sheep)sheep.clone();
// 对于浅拷贝,拷贝出来的引用类型成员变量 hashCode 不变
System.out.println("sheep2 =" + sheep2 + "sheep2.friend=" + sheep2.friend.hashCode());
System.out.println("sheep3 =" + sheep3 + "sheep3.friend=" + sheep3.friend.hashCode());
}
// 2. 深拷贝
public class DeepProtoType implements Serializable, Cloneable {
public String name;
public DeepCloneableTarget deepCloneableTarget;
public DeepProtoType() {
super();
}
/**
* 方式1
*
* @return Object
*/
@Override
protected Object clone() throws CloneNotSupportedException {
Object deep = null;
//这里完成对基本数据类型(属性)和String的克隆
deep = super.clone();
//对引用类型的属性,进行单独处理
DeepProtoType deepProtoType = (DeepProtoType) deep;
deepProtoType.deepCloneableTarget = (DeepCloneableTarget) deepCloneableTarget.clone();
return deepProtoType;
}
/**
* 方式2 通过对象的序列化实现 (推荐)
*
* @return Object
*/
public Object deepClone() {
//创建流对象
ByteArrayOutputStream bos = null;
ObjectOutputStream oos = null;
ByteArrayInputStream bis = null;
ObjectInputStream ois = null;
try {
//序列化
bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bos);
//当前这个对象以对象流的方式输出
oos.writeObject(this);
//反序列化
bis = new ByteArrayInputStream(bos.toByteArray());
ois = new ObjectInputStream(bis);
return ois.readObject();
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
try {
bos.close();
oos.close();
bis.close();
ois.close();
} catch (Exception e2) {
System.out.println(e2.getMessage());
}
}
}
}
public class DeepCloneableTarget implements Serializable, Cloneable {
private static final long serialVersionUID = 1L;
private String cloneName;
private String cloneClass;
public DeepCloneableTarget(String cloneName, String cloneClass) {
this.cloneName = cloneName;
this.cloneClass = cloneClass;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public static void main(String[] args) throws Exception {
DeepProtoType p = new DeepProtoType();
p.name = "喜羊羊";
p.deepCloneableTarget = new DeepCloneableTarget("大羊", "小羊");
DeepProtoType p2 = (DeepProtoType) p.deepClone();
System.out.println("p.name=" + p.name + "p.deepCloneableTarget=" + p.deepCloneableTarget.hashCode());
System.out.println("p2.name=" + p.name + "p2.deepCloneableTarget=" + p2.deepCloneableTarget.hashCode());
}
原型模式在源码中的应用
ArrayList
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
public Object clone() {
try {
ArrayList<?> v = (ArrayList<?>) super.clone();
v.elementData = Arrays.copyOf(elementData, size);
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
}
......
}
HashMap
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable {
@Override
public Object clone() {
HashMap<K,V> result;
try {
result = (HashMap<K,V>)super.clone();
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
result.reinitialize();
result.putMapEntries(this, false);
return result;
}
......
}