前言
本文主要学习原型模式,原型模式是一种创建对象的模式,原型实例指定创建对象的种类,通过拷贝的方式创建新的对象。
一、介绍
原型模式,是一种对象创建型模式,使用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象,主要用于创建重复的对象同时又要求性能的情况。
二、详细分析
1.核心组成
- Prototype: 声明克隆方法的接口,是所有具体原型类的公共父类,
Cloneable
接口,Serializable
接口; - ConcretePrototype : 具体原型类;
- Client: 调用一个原型对象克隆自身从而创建一个新的对象。
浅拷贝
实现cloneable
接口,只能拷贝基本数据类型对象
深拷贝
实现Serializable
接口,自定义deepClone
方法,通过二进制输入输出实现深拷贝;
2.实现步骤
- 创建具体原型类;
- 如果浅拷贝(只能拷贝基本类型)则实现
cloneable
接口,重写clone方法;如果是深拷贝(除了基本类型,还有引用类型)则实现Serializable
接口,自定义序列化方法。 - 客户端调用;
3.代码示例
浅拷贝
具体原型类,实现cloneable
接口,重写clone
方法:
/**
* 具体原型类
*/
public class Student implements Cloneable {
/**
* 名字
*/
private String name;
/**
* 年龄
*/
private int age;
public Student(){
System.out.println("创建学生类");
}
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 String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
/**
* 实现 Cloneable colone 方法
* @return
* @throws CloneNotSupportedException
*/
@Override
protected Student clone() throws CloneNotSupportedException {
return (Student) super.clone();
}
}
客户端:
/**
* 客户端
*/
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
Student student = new Student();
student.setAge(18);
student.setName("王小明");
Student clone = student.clone();
clone.setName("韩美美");
System.out.println(student.toString());
System.out.println(clone.toString());
}
}
结果:
通过输出结果,可以发现初始化的构造方法只调用了1次。
浅拷贝存在一个问题,就是只能拷贝基本数据类型,如果使用了引用数据类型,比如集合,或者对象的时候,拷贝将出现问题,如下:
新增加课程集合:
public class Student implements Cloneable {
/**
* 名字
*/
private String name;
/**
* 年龄
*/
private int age;
/**
* 课程
*/
private List<String> clazz = new ArrayList<>();
public Student(){
System.out.println("创建学生类");
}
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> getClazz() {
return clazz;
}
public void setClazz(List<String> clazz) {
this.clazz = clazz;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", clazz=" + clazz +
'}';
}
/**
* 实现 Cloneable colone 方法
* @return
* @throws CloneNotSupportedException
*/
@Override
protected Student clone() throws CloneNotSupportedException {
return (Student) super.clone();
}
}
客户端:
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
Student student = new Student();
student.setAge(18);
student.setName("王小明");
student.getClazz().add("语文");
Student clone = student.clone();
clone.setName("韩美美");
clone.getClazz().add("数学");
System.out.println(student.toString());
System.out.println(clone.toString());
}
}
结果:
发现被克隆的对象的clazz
列表也发生了变化,所以得出以下结论:
如果原型对象的成员变量是基本数据类型(int、double、byte、boolean、char等),将复制一份给克隆对象;如果原型对象的成员变量是引用类型,则将引用对象的地址复制一份给克隆对象,也就是说原型对象和克隆对象的成员变量指向相同的内存地址。
深拷贝
实现Serializable
接口,使用二进制输入输出实现:
具体原型类:
/**
* 具体原型类
*/
public class Student implements Cloneable,Serializable {
/**
* 名字
*/
private String name;
/**
* 年龄
*/
private int age;
/**
* 课程
*/
private List<String> clazz=new ArrayList<>();
public Student(){
System.out.println("创建学生类");
}
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> getClazz() {
return clazz;
}
public void setClazz(List<String> clazz) {
this.clazz = clazz;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", clazz=" + clazz +
'}';
}
/**
* 实现 Cloneable colone 方法
* @return
* @throws CloneNotSupportedException
*/
@Override
protected Student clone() throws CloneNotSupportedException {
return (Student) super.clone();
}
/**
* 深拷贝
* @return
*/
protected Object deepClone(){
try{
// 输出 序列化
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(this);
// 输入 序列化
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
return objectInputStream.readObject();
}catch (Exception e){
e.printStackTrace();
}
return null;
}
}
客户端:
public static void main(String[] args) throws CloneNotSupportedException {
Student student = new Student();
student.setAge(18);
student.setName("王小明");
student.getClazz().add("语文");
Student clone = (Student) student.deepClone();
clone.setName("韩美美");
clone.getClazz().add("数学");
System.out.println(student.toString());
System.out.println(clone.toString());
}
结果:
可以看到已经解决了 浅拷贝引用类型,拷贝的问题,得出结论:
无论原型对象的成员变量是基本数据类型还是引用类型,都将复制一份给克隆隆对象。
4.优缺点
优点
- 当创建新的对象实例较为复杂时,使用原型模式可以简化对象的创建过程,可以提高新实例的创建效率;
- 可以使用深拷贝,记录保存当前的状态,随时可恢复历史。
缺点
- 需要为每一个类都配备一个克隆方法,对已有的类进行改造,需要修改源码,不符合开闭原则;
- 深拷贝实现比较复杂,当对象之间存在多重的嵌套引用时,需要对每一层对象对应的类都必须支持深克隆。
5.使用场景
- 创建的对象过于庞大的时候,可以通过拷贝对已有的对象进行拷贝,提高效率;
- 如果系统要保存对象的状态,做备用的时候可以使用拷贝,留存。
总结
以上就是今天要讲的内容,本文介绍了原型模式的组成,实现和使用场景,并提供代码示例。