创建型-原型模式
原型模式:使用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
类型:创建型模式。
角色:
抽象原型类:它是声明克隆方法的接口,是所有具体原型类的公共父类,可以是接口,抽象类,甚至是具体实现类。
具体原型类:实现在抽象原型类中声明的克隆方法,在克隆方法中返回自己的一个克隆对象。
客户类:让一个原型对象克隆自身从而创建一个新的对象,在客户类中只需要直接实例化创建一个原型对象,在通过调用该对象的克隆方法即可得到多个相同的对象。
预备知识:
在Java中的数据类型分为基本数据类型(byte,short,int,long,float,double,char,boolean)和引用数据类型(类,接口类型,数组类型,枚举类型,注解类型) ,两者的区别是,基本数据类型在被创建时,在栈上给其划分出一块内存,将数值直接存储在栈上。引用数据类型在被创建时,首先要在栈上给其引用(句柄)分配一块内存,而对象的具体信息都存储在堆内存上,然后由栈上的引用指向堆中对象的地址。
下面以复制一本书为例:
浅拷贝原型模式代码示例:
public class Author {
private String name;
private int 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;
}
}
public class Book implements Cloneable {
private String title;
private int pageNum;
private Author author;
@Override
public Book clone() {
Book book = null;
try {
book = (Book) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return book;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public int getPageNum() {
return pageNum;
}
public void setPageNum(int pageNum) {
this.pageNum = pageNum;
}
public Author getAuthor() {
return author;
}
public void setAuthor(Author author) {
this.author = author;
}
}
public class Test {
public static void main(String[] args) {
Book book1 = new Book();
Author author = new Author();
author.setAge(30);
author.setName("smart");
book1.setAuthor(author);
book1.setTitle("times");
book1.setPageNum(300);
Book book2 = book1.clone();
System.out.println(book1 == book2);
System.out.println(book1.getAuthor() == book2.getAuthor());
System.out.println(book1.getTitle() == book2.getTitle());
System.out.println(book1.getPageNum() == book2.getPageNum());
}
}
运行结果:

分析:
根据输出结果我们可以发现,复制出来的对象,在堆上重新开辟了内存空间,对象中各属性保持相等,对于基本数据类型,很好理解,在栈上重新开辟空间,复制相应的zhi,但是对于引用数据类型,说明它们指向的对象是相同的,并没有重新开辟内存空间,即引用类型的属性所指向的对象,并没有复制。我们称其为浅复制或浅拷贝
深拷贝原型模式代码示例:
import java.io.Serializable;
public class AuthorS implements Serializable {
private String name;
private int 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;
}
}
import java.io.*;
public class BookS implements Serializable {
private String title;
private int pageNum;
private AuthorS authorS;
public BookS deepClone() throws IOException, ClassNotFoundException {
//写入当前对象的二进制流
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(this);
//读出二进制流产生的新对象
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
return (BookS) objectInputStream.readObject();
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public int getPageNum() {
return pageNum;
}
public void setPageNum(int pageNum) {
this.pageNum = pageNum;
}
public AuthorS getAuthorS() {
return authorS;
}
public void setAuthorS(AuthorS authorS) {
this.authorS = authorS;
}
}
public class TestS {
public static void main(String[] args) throws Exception{
BookS bookS1 = new BookS();
AuthorS authorS = new AuthorS();
authorS.setAge(30);
authorS.setName("smart");
bookS1.setAuthorS(authorS);
bookS1.setTitle("times");
bookS1.setPageNum(300);
BookS bookS2 = bookS1.deepClone();
System.out.println(bookS1 == bookS2);
System.out.println(bookS1.getAuthorS() == bookS2.getAuthorS());
System.out.println(bookS1.getTitle() == bookS2.getTitle());
System.out.println(bookS1.getPageNum() == bookS2.getPageNum());
}
}
运行结果:

分析:
深拷贝不仅在堆上开辟了空间以存储复制出来的对象,甚至连对象中的引用类型的属性所指向的对象也得以复制,重新开辟了堆空间存储。
总结:
优点:
1,当创建的新的对象实例比较复杂时,使用原型模式可以简化对象的创建过程,通过复制一个已有实例可以提高新实例的创建效率。
2,提供了简化的创建结构
3,使用深拷贝的方式保存对象的状态,可辅助实现撤销操作。
缺点:
1,需要为每一个类配备一个克隆方法,而且该克隆方法位于一个类的内部,当对已有的类进行改造时,需要修改源代码,违背了“开闭原则”
2,在实现深拷贝时需要编写较为复杂的代码,而且当对象之间存在多重的嵌套引用时,为了实现深拷贝,每一层对象对应的类都必须支持深拷贝,实现起来可能会比较麻烦。