1. 意图(Intent)
原型模式的核心思想,拷贝指定的” 原型实例(对象)“,创建跟该对象一样的新对象。
为什么我们需要原型模式 ?
浏览器中,如果我们生成了一个button实例,这个 button实例经过一系列操作,携带了各种信息,比如 button 加颜色,加背景图,加文字,加事件等等。如果我们这时候需要和这个 button 实例完全一样的一个实例,仅仅通过类 new 一个 button 出来是远远不够的,因为我们还要对它进行一系列的操作,所以这个生成一个完全一样的实例的过程是非常复杂的 。
什么是浅拷贝和深拷贝 ?
浅拷贝
- 当类的成员变量是基本数据类型时,浅拷贝会复制该属性的值赋值给新对象。
- 当成员变量是引用数据类型时,浅拷贝复制的是引用数据类型的地址值。
即多个对象共同引用同一个内存
浅拷贝中当某一个类修改了引用数据类型的成员变量后,会导致所有拷贝出的类都发生改变。
深拷贝
- 深拷贝不仅会复制成员变量为基本数据类型的值,给新对象。
- 还会给是引用数据类型的成员变量申请储存空间,并复制引用数据类型成员变量的对象。
这样拷贝出的新对象就不怕修改了是引用数据类型的成员变量后,对其它拷贝出的对象造成影响了。
Java 语言提供的 Clone 方法
所有的 Java 类都继承自 java.lang.Object 。事实上,Object 类提供一个clone( )方法,可以将一个 Java 对象复制一份。因此在 Java 中可以直接使用 Object 提供的 clone( ) 方法来实现对象的克隆,实现原型模式。
注意:能够实现克隆的Java类必须实现一个标识接口Cloneable,表示这个 Java 类支持被复制。如果一个类没有实现这个接口但是调用了clone( ) 方法,Java编译器将抛出一个CloneNotSupportedException异常。
java 的克隆是浅克隆,碰到对象引用的时候,克隆出来的对象和原对象中的引用将指向同一个对象。通常实现深克隆的方法是将对象进行序列化,然后再进行反序列化。
Java语言中的clone()方法满足:
- 对任何对象 x,都有 x.clone() != x ,克隆对象与原型对象不是同一个对象;
- 对任何对象 x,都有 x.clone( ).getClass( ) == x.getClass( ),克隆对象与原型对象类型一样;
- 如果对象 x 的 equals( ) 方法定义恰当,那么 x.clone().equals( x ) 应该成立。
直接利用Object类的clone()方法,具体步骤如下:
- 派生类需实现 Cloneable 接口。
- 在派生类中覆盖基类的 clone( ) 方法,并声明为 public;
- 在派生类的 clone( ) 方法中,调用
**super.clone( )**;
此时,Object 类相当于抽象原型类,所有实现了 Cloneable 接口的类相当于具体原型类。
2. 类图(Class Diagram)

Prototype(抽象原型类):
- 它是声明克隆方法的接口,是所有具体原型类的公共父类,
- 可以是抽象类也可以是接口,甚至还可以是具体实现类。
ConcretePrototype(具体原型类):
它实现在抽象原型类中声明的克隆方法,在克隆方法中返回自己的一个克隆对象。
Client(客户类):
- 通过调用该对象的克隆方法即可得到多个相同的对象。
- 由于客户类针对抽象原型类Prototype编程,因此用户可以根据需要选择具体原型类
3. 实现(Implementation)
public abstract class Prototype {abstract Prototype Clone();}
public class ConcretePrototype extends Prototype {private String filed;public ConcretePrototype(String filed) {this.filed = filed;}@OverridePrototype Clone() {return new ConcretePrototype(filed);}@Overridepublic String toString() {return filed;}}
public class Client {public static void main(String[] args) {Prototype prototype = new ConcretePrototype("abc");Prototype clone = prototype.Clone();System.out.println(clone.toString());}}//输出: abc
I 浅克隆
//1.派生类实现 Cloneable 接口public class Realizetype implements Cloneable {@Overrideprotected Realizetype clone() throws CloneNotSupportedException {//2.在派生类的 clone() 方法中,调用 super.clone()return (Realizetype) super.clone();}}
II 序列化的深克隆
奖状类
//实现克隆和序列化接口@Datapublic class Citation implements Cloneable, Serializable {//2.定义引用类型变量private Student stu;//3.定义构造函数//4.设置Set、Get方法void show() {System.out.println(stu.getName() + "同学:被评为三好学生。特发此状!");}//5.重写Clone方法@Overridepublic Citation clone() throws CloneNotSupportedException {return (Citation) super.clone();}}
学生类
//定义序列化接口@Datapublic class Student implements Serializable {private String name;private String address;//2.定义构造函数...//3.设置Get、Set方法}
客户端
public class Client {public static void main(String[] args) throws Exception {Citation c1 = new Citation();Student stu = new Student("张三", "西安");c1.setStu(stu);//创建对象输出流对象ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\\Users\\b.txt"));//将c1对象写出到文件中oos.writeObject(c1);//关闭流oos.close();//创建对象出入流对象ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:\\Users\\b.txt"));//读取对象Citation c2 = (Citation) ois.readObject();//关闭流ois.close();//获取c2奖状所属学生对象Student stu1 = c2.getStu();stu1.setName("李四");//判断stu对象和stu1对象是否是同一个对象System.out.println("stu和stu1是同一个对象?" + (stu == stu1));c1.show();c2.show();}}
III 实现 Clone 接口的深克隆
深克隆的做法很简单,既然需要 Student 也被克隆一份,那么 Student 也继承 Cloneable 接口,重写 clone 方法,先克隆 Citation,再克隆 Student
奖状类
//实现克隆接口@Datapublic class Citation implements Cloneable {//1.定义引用类型变量private Student stu;//2.定义构造函数//3.设置Set、Get方法void show() {System.out.println(stu.getName() + "同学:被评为三好学生。特发此状!");}//5.重写Clone方法@Overridepublic Citation clone() throws CloneNotSupportedException {//6.先克隆主体Citation citation=(Citation)super.clone();//7.再克隆主体的引用citation.stu= (Student) stu.clone();return citation;}}
学生类
//定义序列化接口@Datapublic class Student implements Cloneable {private String name;private String address;//1.定义构造函数...//2.设置Get、Set方法...//3.重写clone方法@Overrideprotected Object clone() throws CloneNotSupportedException {return (Student) super.clone();}}
