文章目录
- 前言
- 一、介绍
- 二、详细分析
- 1.核心组成
- 浅拷贝
- 深拷贝
- 2.实现步骤
- 3.代码示例
- 浅拷贝
- 深拷贝
- 4.优缺点
- 优点
- 缺点
- 5.使用场景
- 总结
前言
本文主要学习原型模式,原型模式是一种创建对象的模式,原型实例指定创建对象的种类,通过拷贝的方式创建新的对象。
一、介绍
原型模式,是一种对象创建型模式,使用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象,主要用于创建重复的对象同时又要求性能的情况。
二、详细分析
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;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +'}';}/*** 实现 Cloneable colone 方法* @return* @throws CloneNotSupportedException*/@Overrideprotected 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;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +", clazz=" + clazz +'}';}/*** 实现 Cloneable colone 方法* @return* @throws CloneNotSupportedException*/@Overrideprotected 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;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +", clazz=" + clazz +'}';}/*** 实现 Cloneable colone 方法* @return* @throws CloneNotSupportedException*/@Overrideprotected 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.使用场景
- 创建的对象过于庞大的时候,可以通过拷贝对已有的对象进行拷贝,提高效率;
- 如果系统要保存对象的状态,做备用的时候可以使用拷贝,留存。
总结
以上就是今天要讲的内容,本文介绍了原型模式的组成,实现和使用场景,并提供代码示例。