23种设计模式(3)-原型模式

定义

原型模式是用于创建重复的对象,同时又能保证性能。

意图:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

实现方式

主要有两种情况:一种是浅克隆;一种是深克隆

浅克隆

主要理解:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址

前提:实现Cloneable接口

关键代码:重写Object对象的clone()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.util.Date;
public class Person implements Cloneable { //实现Cloneable接口,就可以实现对象的浅克隆
private String name;
private int age;
private Date birthday;

@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}

public static void main(String[] args) throws CloneNotSupportedException {
Person person = new Person();
Person person1 = (Person) person.clone(); //克隆一个对象
System.out.println(person == person1); //false
}
}

image-20211101201003624

  • 运行结果显示false,克隆后返回的的对象和原来的对象是两个对象

  • 但是对于非基本类型属性,仍指向原来属性所指向的对象的地址(解决方法看深克隆的重写clone方法)

深克隆

主要理解:创建一个新对象,属性中的引用的其他对象也会被克隆,不再指向原有对象的地址

重写clone()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import java.util.Date;
public class Person implements Cloneable { //实现Cloneable接口,就可以实现对象的浅克隆
private String name;
private int age;

public Date getBirthday() {
return birthday;
}

public void setBirthday(Date birthday) {
this.birthday = birthday;
}

private Date birthday;

@Override
protected Object clone() throws CloneNotSupportedException {
Person person = null;
person = (Person) super.clone();
//经修改,克隆Person的对象的时候,内部的非基本类型属性也会被克隆,并将引用地址给到克隆出来的对象的属性中
//如此一来,就可以实现在修改克隆后的对象时,不会影响原来的对象
if (this.getBirthday() != null) {
person.setBirthday((Date) this.getBirthday().clone());
}
return person;
}
}

通过重写clone方法,将对象中非基本类型属性也clone一下(如果非基本类型属性是一个对象,且其还有其他非基本类型属性,则需继续将非基本类型属性的clone方法也进行重写,即“套娃”)

反序列化方式

前提:被序列化和反序列化的类需要实现Serializable接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package com;

import java.io.*;
import java.util.Date;

public class Person implements Serializable { //实现Cloneable接口,就可以实现对象的浅克隆
private String name;
private int age;

public Date getBirthday() {
return birthday;
}

public void setBirthday(Date birthday) {
this.birthday = birthday;
}

private Date birthday;

public static void main(String[] args) throws IOException, ClassNotFoundException {
Person person = new Person();
Person person1 = (Person) Util.clone(person);
System.out.println(person1);
}
}

//实现序列化和反序列化深克隆的工具类
class Util {
public static Object clone(Object obj) throws IOException, ClassNotFoundException {

//使用数组作为中间数据过渡的工具
//Byte[] bytes = new Byte[1024 * 10];

//序列化
//也可以不用此输入流,换用其他输入流,并且用一个字节数组来代替其作用
ByteArrayOutputStream stream = new ByteArrayOutputStream();
//使用ObjectOutputStream的writeObject进行序列化
ObjectOutputStream objectOutputStream = new ObjectOutputStream(stream);
objectOutputStream.writeObject(obj); //写进了stream流

//反序列化
//首先将存入stream输出流中的数据转为字节数组,然后用一个输入流读取其数据
ByteArrayInputStream inputStream = new ByteArrayInputStream(stream.toByteArray());
//使用ObjectInputStream的writeObject进行序列化
ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
Object object = objectInputStream.readObject();

return object;
}
}

image-20211101222809004

  • 上述序列化和反序列化的实现操作,可以放在单独的工具类中,也可以放在clone方法中(但这样需要实现Cloneable接口)
  • 其中,如果对象由非基本类型属性,则非基本类型属性也需要实现Serializable接口

应用场景

  1. 资源优化场景
  2. 类初始化需要小号非常多的资源(包括数据、硬件资源等)
  3. 性能和安全要求的场景
  4. 通过new产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式
  5. 一个对象多个修改这的场景
  6. 实际项目中,原型模式很少单独出现,一般是和工厂模式一块出现:通过clone方法创建一个对象,然后由工厂方法提供给调用者使用


补充:Object类的clone方法为什么一定要实现Cloneable接口?

我们看官方对Object类的clone方法的说明:

image-20211101201632351

翻译后:

image-20211101201710704

我的理解:clone方法是在Cloneable接口中声明的,但是Object类本身没有实现这个接口,所以如果直接调用Object的clone方法(不继承Cloneable接口),则会包运行时异常CloneNotSupportedException。

在Cloneable接口中也有明确说明,翻译后:

image-20211101201958556

Contents
  1. 1. 定义
  2. 2. 实现方式
    1. 2.1. 浅克隆
    2. 2.2. 深克隆
      1. 2.2.1. 重写clone()方法
      2. 2.2.2. 反序列化方式
  3. 3. 应用场景
  4. 4. 补充:Object类的clone方法为什么一定要实现Cloneable接口?
|