设计模式总结篇系列:原型模式(Prototype)
首先对原型模式进行一个简单概念说明:通过一个已经存在的对象,复制出更多的具有与此对象具有相同类型的新的对象。
在理解Java原型模式之前,首先需要理解Java中的一个概念:复制/克隆。
在博文《Java总结篇系列:java.lang.Object》一文中,对Java中的clone()方法进行了一定的阐述。同时,我们需要知道,Java中的对象复制/克隆分为浅复制和深复制。
一、浅复制:
我们知道,一个类的定义中包括属性和方法。属性用于表示对象的状态,方法用于表示对象所具有的行为。其中,属性既可以是Java中基本数据类型,也可以是引用类型。Java中的浅复制通常使用clone()方式完成。
当进浅复制时,clone函数返回的是一个引用,指向的是新的clone出来的对象,此对象与原对象分别占用不同的堆空间。同时,复制出来的对象具有与原对象一致的状态。
此处对象一致的状态是指:复制出的对象与原对象中的属性值完全相等==。
下面以复制一本书为例:
1.定义Book类和Author类:
1 class Author {
2
3 private String name;
4 private int age;
5
6 public String getName() {
7 return name;
8 }
9
10 public void setName(String name) {
11 this.name = name;
12 }
13
14 public int getAge() {
15 return age;
16 }
17
18 public void setAge(int age) {
19 this.age = age;
20 }
21
22 }
1 class Book implements Cloneable {
2
3 private String title;
4 private int pageNum;
5 private Author author;
6
7 public Book clone() {
8 Book book = null;
9 try {
10 book = (Book) super.clone();
11 } catch (CloneNotSupportedException e) {
12 // TODO Auto-generated catch block
13 e.printStackTrace();
14 }
15 return book;
16 }
17
18 public String getTitle() {
19 return title;
20 }
21
22 public void setTitle(String title) {
23 this.title = title;
24 }
25
26 public int getPageNum() {
27 return pageNum;
28 }
29
30 public void setPageNum(int pageNum) {
31 this.pageNum = pageNum;
32 }
33
34 public Author getAuthor() {
35 return author;
36 }
37
38 public void setAuthor(Author author) {
39 this.author = author;
40 }
41
42 }
2.测试:
1 package com.qqyumidi;
2
3 public class PrototypeTest {
4
5 public static void main(String[] args) {
6 Book book1 = new Book();
7 Author author = new Author();
8 author.setName("corn");
9 author.setAge(100);
10 book1.setAuthor(author);
11 book1.setTitle("好记性不如烂博客");
12 book1.setPageNum(230);
13
14 Book book2 = book1.clone();
15
16 System.out.println(book1 == book2); // false
17 System.out.println(book1.getPageNum() == book2.getPageNum()); // true
18 System.out.println(book1.getTitle() == book2.getTitle()); // true
19 System.out.println(book1.getAuthor() == book2.getAuthor()); // true
20
21 }
22 }
由输出的结果可以验证说到的结论。由此我们发现:虽然复制出来的对象重新在堆上开辟了内存空间,但是,对象中各属性确保持相等。对于基本数据类型很好理解,但对于引用数据类型来说,则意味着此引用类型的属性所指向的对象本身是相同的, 并没有重新开辟内存空间存储。换句话说,引用类型的属性所指向的对象并没有复制。
由此,我们将其称之为浅复制。当复制后的对象的引用类型的属性所指向的对象也重新得以复制,此时,称之为深复制。
二、深复制:
Java中的深复制一般是通过对象的序列化和反序列化得以实现。序列化时,需要实现Serializable接口。
下面还是以Book为例,看下深复制的一般实现过程:
1.定义Book类和Author类(注意:不仅Book类需要实现Serializable接口,Author同样也需要实现Serializable接口!!):
1 class Author implements Serializable{
2
3 private String name;
4 private int age;
5
6 public String getName() {
7 return name;
8 }
9
10 public void setName(String name) {
11 this.name = name;
12 }
13
14 public int getAge() {
15 return age;
16 }
17
18 public void setAge(int age) {
19 this.age = age;
20 }
21
22 }
1 class Book implements Serializable {
2
3 private String title;
4 private int pageNum;
5 private Author author;
6
7 public Book deepClone() throws IOException, ClassNotFoundException{
8 // 写入当前对象的二进制流
9 ByteArrayOutputStream bos = new ByteArrayOutputStream();
10 ObjectOutputStream oos = new ObjectOutputStream(bos);
11 oos.writeObject(this);
12
13 // 读出二进制流产生的新对象
14 ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
15 ObjectInputStream ois = new ObjectInputStream(bis);
16 return (Book) ois.readObject();
17 }
18
19 public String getTitle() {
20 return title;
21 }
22
23 public void setTitle(String title) {
24 this.title = title;
25 }
26
27 public int getPageNum() {
28 return pageNum;
29 }
30
31 public void setPageNum(int pageNum) {
32 this.pageNum = pageNum;
33 }
34
35 public Author getAuthor() {
36 return author;
37 }
38
39 public void setAuthor(Author author) {
40 this.author = author;
41 }
42
43 }
2.测试:
1 public class PrototypeTest {
2
3 public static void main(String[] args) throws ClassNotFoundException, IOException {
4 Book book1 = new Book();
5 Author author = new Author();
6 author.setName("corn");
7 author.setAge(100);
8 book1.setAuthor(author);
9 book1.setTitle("好记性不如烂博客");
10 book1.setPageNum(230);
11
12 Book book2 = book1.deepClone();
13
14 System.out.println(book1 == book2); // false
15 System.out.println(book1.getPageNum() == book2.getPageNum()); // true
16 System.out.println(book1.getTitle() == book2.getTitle()); // false
17 System.out.println(book1.getAuthor() == book2.getAuthor()); // false
18
19 }
20 }
从输出结果中可以看出,深复制不仅在堆内存上开辟了空间以存储复制出的对象,甚至连对象中的引用类型的属性所指向的对象也得以复制,重新开辟了堆空间存储。
至此:设计模式中的创建型模式总结完毕,一共有五种创建型模式,分别为:单例模式(SingleTon)、建造者模式(Builder)、工厂方法模式(Factory Method)、抽象工厂模式(Abstract Factory)和原型模式(Prototype)。每种模式适用于不同的场景,具体应用时需注意区分。
笔者水平有限,若有错漏,欢迎指正,如果转载以及CV操作,请务必注明出处,谢谢!