Hexo

点滴积累 豁达处之

0%

05 原型模式

  原型模式:使用原型实例指定待创建对象的类型,并且通过复制这个原型来创建新的对象。

原型模式

1 原型模式结构:

原型模式主要包含3个角色:

原型模式

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

(2)ConcretePrototype(具体原型类):它实现抽象原型类中声明的克隆方法,在克隆方法中返回自己的一个克隆对象。

(3)Client(客户端):在客户类中,让一个原型对象克隆自身从而创建一个新的对象。

2 浅克隆:

浅克隆:当原型对象被复制时,只复制它本身和其中包含的值类型的成员变量,而引用类型的成员变量并没有复制。

​ 我们知道,一个类的定义中包括属性和方法。属性用于表示对象的状态,方法用于表示对象所具有的行为。其中,属性既可以是Java中基本数据类型,也可以是引用类型。Java中的浅复制通常使用clone()方式完成。

​ 当进浅复制时,clone函数返回的是一个引用,指向的是新的clone出来的对象,此对象与原对象分别占用不同的堆空间。同时,复制出来的对象具有与原对象一致的状态。

此处对象一致的状态是指:复制出的对象与原对象中的属性值完全相等==。

浅克隆

2.1 浅复制示例

1.定义Book类和Author类:

1
2
3
4
class Author {
private String name;
private int age;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Book implements Cloneable {
private String title;
private int pageNum;
private Author author;

public Book clone() {
Book book = null;
try {
book = (Book) super.clone();
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return book;
}
}

2.测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class PrototypeTest {

public static void main(String[] args) {
Book book1 = new Book();
Author author = new Author();
author.setName("corn");
author.setAge(100);
book1.setAuthor(author);
book1.setTitle("好记性不如烂博客");
book1.setPageNum(230);

Book book2 = book1.clone();

System.out.println(book1 == book2); // false
System.out.println(book1.getPageNum() == book2.getPageNum()); // true
System.out.println(book1.getTitle() == book2.getTitle()); // true
System.out.println(book1.getAuthor() == book2.getAuthor()); // true

}
}

由输出的结果可以验证说到的结论。由此我们发现:虽然复制出来的对象重新在堆上开辟了内存空间,但是,对象中各属性确保持相等。对于基本数据类型很好理解,但对于引用数据类型来说,则意味着此引用类型的属性所指向的对象本身是相同的, 并没有重新开辟内存空间存储。换句话说,引用类型的属性所指向的对象并没有复制。

由此,我们将其称之为浅复制。当复制后的对象的引用类型的属性所指向的对象也重新得以复制,此时,称之为深复制。

3 深克隆

深克隆:除了对象本身被复制外,对象所包含的所有成员变量也将被复制。

Java中的深复制一般是通过对象的序列化和反序列化得以实现。序列化时,需要实现Serializable接口。

深克隆

3.1 深复制示例

下面还是以Book为例,看下深复制的一般实现过程:

1.定义Book类和Author类(**注意:不仅Book类需要实现Serializable接口,Author同样也需要实现Serializable接口!!**):

1
2
3
4
class Author implements Serializable{
private String name;
private int age;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Book implements Serializable {

private String title;
private int pageNum;
private Author author;

public Book deepClone() throws IOException, ClassNotFoundException{
// 写入当前对象的二进制流
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);

// 读出二进制流产生的新对象
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return (Book) ois.readObject();
}
}

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class PrototypeTest {

public static void main(String[] args) throws ClassNotFoundException, IOException {
Book book1 = new Book();
Author author = new Author();
author.setName("corn");
author.setAge(100);
book1.setAuthor(author);
book1.setTitle("好记性不如烂博客");
book1.setPageNum(230);

Book book2 = book1.deepClone();

System.out.println(book1 == book2); // false
System.out.println(book1.getPageNum() == book2.getPageNum()); // true
System.out.println(book1.getTitle() == book2.getTitle()); // false
System.out.println(book1.getAuthor() == book2.getAuthor()); // false

}
}

从输出结果中可以看出,深复制不仅在堆内存上开辟了空间以存储复制出的对象,甚至连对象中的引用类型的属性所指向的对象也得以复制,重新开辟了堆空间存储。

参考链接