一种对象创建型模式,使用原型实例指定创建对象的种类。拷贝对象。实际是对Cloneable接口的使用。
该模式也运用到了深拷贝和浅拷贝这个知识点。
核心组成:
- Prototype: 声明克隆方法的接口,即Cloneable接口,原型模式即为对整个接口的实现,其中实践了深拷贝和浅拷贝知识点
- ConcretePrototype:具体的原型类,实现了克隆接口
- Client: 原型类克隆而得到的实例
浅拷贝实现 Cloneable,深拷贝是通过实现 Serializable 读取二进制流
浅拷贝
如果原型对象的成员变量是基本数据类型(int、double、byte、boolean、char等),将复制一份给克隆对象;
如果原型对象的成员变量是引用类型,则将引用对象的地址复制一份给克隆对象,也就是说原型对象和克隆对象的成员变量指向相同的内存地址
通过覆盖Object类的clone()方法可以实现浅克隆
深拷贝
无论原型对象的成员变量是基本数据类型还是引用类型,都将复制一份给克隆对象,如果需要实现深克隆,可以通过序列化(Serializable)等方式来实现
原型模式是内存二进制流的拷贝,比new对象性能高很多,使用的时候记得注意是选择浅拷贝还是深拷贝
// 原型对象
public class Person implements Cloneable {
String name;
int age;
public Person() {
System.out.println("---调用构造函数----");
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public Person clone() throws CloneNotSupportedException {
return (Person) super.clone();
}
}
可以看到,在生成person1 和person2的过程中只调用了一次构造方法。即得到了两个对象。
但是如果原型对象包含引用类型时,那么会出现意外的效果。因此需要改造原型类
public class Person implements Cloneable, Serializable {
String name;
int age;
List<String> list;
public Person() {
System.out.println("---调用构造函数----");
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", list=" + list +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public List<String> getList() {
return list;
}
public void setList(List<String> list) {
this.list = list;
}
@Override
public Person clone() throws CloneNotSupportedException {
return (Person) super.clone();
}
// 声明深拷贝方法
public Person deepClone() {
// 输出序列化
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(this);
// 输入,反序列化
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
Person person = (Person) ois.readObject();
return person;
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
return null;
}
}
}
总结:
优点
- 当创建新的对象实例较为复杂时,使用原型模式可以简化对象的创建过程,可以提高新实例的创建效率
- 辅助实现撤销操作,使用深克隆的方式保存对象的状态,使用原型模式将对象复制一份并将其状态保存起来,以便在需要的时候使用恢复到历史状态
缺点
- 需要为每一个类配备一个克隆方法,对已有的类进行改造时,需要修改源代码,违背了“开闭原则”