1. 意图(Intent)

原型模式的核心思想,拷贝指定的” 原型实例(对象),创建跟该对象一样的新对象。

为什么我们需要原型模式 ?

浏览器中,如果我们生成了一个button实例,这个 button实例经过一系列操作,携带了各种信息,比如 button 加颜色,加背景图,加文字,加事件等等。如果我们这时候需要和这个 button 实例完全一样的一个实例,仅仅通过类 new 一个 button 出来是远远不够的,因为我们还要对它进行一系列的操作,所以这个生成一个完全一样的实例的过程是非常复杂的 。

什么是浅拷贝和深拷贝 ?

浅拷贝

  1. 当类的成员变量是基本数据类型时,浅拷贝会复制该属性的值赋值给新对象。
  2. 当成员变量是引用数据类型时,浅拷贝复制的是引用数据类型的地址值

    即多个对象共同引用同一个内存

浅拷贝中当某一个类修改了引用数据类型的成员变量后,会导致所有拷贝出的类都发生改变。

深拷贝

  1. 深拷贝不仅会复制成员变量为基本数据类型的值,给新对象。
  2. 还会给是引用数据类型的成员变量申请储存空间,并复制引用数据类型成员变量的对象

这样拷贝出的新对象就不怕修改了是引用数据类型的成员变量后,对其它拷贝出的对象造成影响了。

Java 语言提供的 Clone 方法

所有的 Java 类都继承自 java.lang.Object 。事实上,Object 类提供一个clone( )方法,可以将一个 Java 对象复制一份。因此在 Java 中可以直接使用 Object 提供的 clone( ) 方法来实现对象的克隆,实现原型模式。

注意:能够实现克隆的Java类必须实现一个标识接口Cloneable,表示这个 Java 类支持被复制。如果一个类没有实现这个接口但是调用了clone( ) 方法,Java编译器将抛出一个CloneNotSupportedException异常。

java 的克隆是浅克隆,碰到对象引用的时候,克隆出来的对象和原对象中的引用将指向同一个对象。通常实现深克隆的方法是将对象进行序列化,然后再进行反序列化

Java语言中的clone()方法满足:

  1. 对任何对象 x,都有 x.clone() != x ,克隆对象与原型对象不是同一个对象;
  2. 对任何对象 x,都有 x.clone( ).getClass( ) == x.getClass( ),克隆对象与原型对象类型一样;
  3. 如果对象 x 的 equals( ) 方法定义恰当,那么 x.clone().equals( x ) 应该成立。

直接利用Object类的clone()方法,具体步骤如下:

  1. 派生类需实现 Cloneable 接口。
  2. 在派生类中覆盖基类的 clone( ) 方法,并声明为 public
  3. 在派生类的 clone( ) 方法中,调用 **super.clone( )**

此时,Object 类相当于抽象原型类,所有实现了 Cloneable 接口的类相当于具体原型类

2. 类图(Class Diagram)

image.png

Prototype(抽象原型类):

  1. 它是声明克隆方法的接口,是所有具体原型类的公共父类,
  2. 可以是抽象类也可以是接口,甚至还可以是具体实现类。

ConcretePrototype(具体原型类):

它实现在抽象原型类中声明的克隆方法,在克隆方法中返回自己的一个克隆对象。

Client(客户类):

  1. 通过调用该对象的克隆方法即可得到多个相同的对象。
  2. 由于客户类针对抽象原型类Prototype编程,因此用户可以根据需要选择具体原型类

3. 实现(Implementation)

  1. public abstract class Prototype {
  2. abstract Prototype Clone();
  3. }
  1. public class ConcretePrototype extends Prototype {
  2. private String filed;
  3. public ConcretePrototype(String filed) {
  4. this.filed = filed;
  5. }
  6. @Override
  7. Prototype Clone() {
  8. return new ConcretePrototype(filed);
  9. }
  10. @Override
  11. public String toString() {
  12. return filed;
  13. }
  14. }
  1. public class Client {
  2. public static void main(String[] args) {
  3. Prototype prototype = new ConcretePrototype("abc");
  4. Prototype clone = prototype.Clone();
  5. System.out.println(clone.toString());
  6. }
  7. }
  8. //输出: abc

I 浅克隆

  1. //1.派生类实现 Cloneable 接口
  2. public class Realizetype implements Cloneable {
  3. @Override
  4. protected Realizetype clone() throws CloneNotSupportedException {
  5. //2.在派生类的 clone() 方法中,调用 super.clone()
  6. return (Realizetype) super.clone();
  7. }
  8. }

II 序列化的深克隆

奖状类

  1. //实现克隆和序列化接口
  2. @Data
  3. public class Citation implements Cloneable, Serializable {
  4. //2.定义引用类型变量
  5. private Student stu;
  6. //3.定义构造函数
  7. //4.设置Set、Get方法
  8. void show() {
  9. System.out.println(stu.getName() + "同学:被评为三好学生。特发此状!");
  10. }
  11. //5.重写Clone方法
  12. @Override
  13. public Citation clone() throws CloneNotSupportedException {
  14. return (Citation) super.clone();
  15. }
  16. }

学生类

  1. //定义序列化接口
  2. @Data
  3. public class Student implements Serializable {
  4. private String name;
  5. private String address;
  6. //2.定义构造函数
  7. ...
  8. //3.设置Get、Set方法
  9. }

客户端

  1. public class Client {
  2. public static void main(String[] args) throws Exception {
  3. Citation c1 = new Citation();
  4. Student stu = new Student("张三", "西安");
  5. c1.setStu(stu);
  6. //创建对象输出流对象
  7. ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\\Users\\b.txt"));
  8. //将c1对象写出到文件中
  9. oos.writeObject(c1);
  10. //关闭流
  11. oos.close();
  12. //创建对象出入流对象
  13. ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:\\Users\\b.txt"));
  14. //读取对象
  15. Citation c2 = (Citation) ois.readObject();
  16. //关闭流
  17. ois.close();
  18. //获取c2奖状所属学生对象
  19. Student stu1 = c2.getStu();
  20. stu1.setName("李四");
  21. //判断stu对象和stu1对象是否是同一个对象
  22. System.out.println("stu和stu1是同一个对象?" + (stu == stu1));
  23. c1.show();
  24. c2.show();
  25. }
  26. }

III 实现 Clone 接口的深克隆

深克隆的做法很简单,既然需要 Student 也被克隆一份,那么 Student 也继承 Cloneable 接口,重写 clone 方法,先克隆 Citation,再克隆 Student

奖状类

  1. //实现克隆接口
  2. @Data
  3. public class Citation implements Cloneable {
  4. //1.定义引用类型变量
  5. private Student stu;
  6. //2.定义构造函数
  7. //3.设置Set、Get方法
  8. void show() {
  9. System.out.println(stu.getName() + "同学:被评为三好学生。特发此状!");
  10. }
  11. //5.重写Clone方法
  12. @Override
  13. public Citation clone() throws CloneNotSupportedException {
  14. //6.先克隆主体
  15. Citation citation=(Citation)super.clone();
  16. //7.再克隆主体的引用
  17. citation.stu= (Student) stu.clone();
  18. return citation;
  19. }
  20. }

学生类

  1. //定义序列化接口
  2. @Data
  3. public class Student implements Cloneable {
  4. private String name;
  5. private String address;
  6. //1.定义构造函数
  7. ...
  8. //2.设置Get、Set方法
  9. ...
  10. //3.重写clone方法
  11. @Override
  12. protected Object clone() throws CloneNotSupportedException {
  13. return (Student) super.clone();
  14. }
  15. }

4. JDK