薄悻不来门半掩,斜阳。负你残春泪几行。

——冯延巳《南乡子》

设计模式-原型模式-深/浅克隆

1. 原型模式在 Spring 框架中应用

1.1 实体类Student
1
2
3
4
5
6
7
8
9
10
11
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private String name;

private String sex;

private String address;

}
1.2 配置文件applicationContext.xml
1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="student" class="cn.justweb.pojo.Student" p:name="涛哥" p:sex="nan" p:address="南京" scope="prototype"></bean>

</beans>

以及spring的依赖。

1.3 测试
1
2
3
4
5
6
7
8
9
10
11
12

public class TestStudent {
@Test
public void test(){
ApplicationContext ctx = new ClassPathXmlApplicationContext( "applicationContext.xml" );

//同时指定bean的id值和类型,来拿到对象
Student student = ctx.getBean( "student", Student.class );

System.out.println( "student = " + student );
}
}
1.4 通过debug调试追踪getBean()方法
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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
// 1. 首先进到方法里面
@Override
public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
assertBeanFactoryActive();
return getBeanFactory().getBean(name, requiredType);
}
// 2. 通过getBeanFactory()方法拿到ConfigurableListableBeanFactory对象
@Override
public final ConfigurableListableBeanFactory getBeanFactory() {
synchronized (this.beanFactoryMonitor) {
if (this.beanFactory == null) {
throw new IllegalStateException("BeanFactory not initialized or already closed - " +
"call 'refresh' before accessing beans via the ApplicationContext");
}
return this.beanFactory;
}
}
// 3. 调用getBean()
@Override
public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
return doGetBean(name, requiredType, null, false);
}

// 4.进入到此方法中 Create bean instance.
// prototypeInstance = createBean(beanName, mbd, args);
@SuppressWarnings("unchecked")
protected <T> T doGetBean(
final String name,
final Class<T> requiredType,
final Object[] args,
boolean typeCheckOnly) throws BeansException
{

// 省略....
// Create bean instance.
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}

else if (mbd.isPrototype()) {
// It's a prototype -> create a new instance.
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}

else {
String scopeName = mbd.getScope();
final Scope scope = this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope '" + scopeName + "'");
}


// 省略.....

return (T) bean;
}

2. 原型模式

  1. 原型模式(Prototype 模式)是指:用 原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象。
  2. 原型模式是一种创建型设计模式,允许一个对象再创建另外一个可定制的对象,无需知道如何创建的细节。
  3. 工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建,即 对象.clone()

Sheep实体类

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
51
52
53

/**
* @Date 2020/6/11 11:17
* @Version 10.21
* @Author DuanChaojie
*/
public class Sheep implements Cloneable{
private String name;
private Integer age;
private String color;

public Sheep() {
}

public Sheep(String name, Integer age, String color) {
this.name = name;
this.age = age;
this.color = color;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Integer getAge() {
return age;
}

public void setAge(Integer age) {
this.age = age;
}

public String getColor() {
return color;
}

public void setColor(String color) {
this.color = color;
}

@Override
public String toString() {
return "Sheep{" +
"name='" + name + '\'' +
", age=" + age +
", color='" + color + '\'' +
'}';
}
}
2.1 传统方式解决克隆羊问题
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
   /**
* @Date 2020/6/11 11:19
* @Version 10.21
* @Author DuanChaojie
*/
public class Client {
public static void main(String[] args) {
Sheep sheep = new Sheep("MM", 3, "yellow");

// 我们很容易想到的克隆对象
Sheep sheep1 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
Sheep sheep2 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
Sheep sheep3 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());

/**
* sheep.hashCode() = 1163157884
* sheep1.hashCode() = 1956725890
* sheep2.hashCode() = 356573597
* sheep3.hashCode() = 1735600054
*/
System.out.println("sheep.hashCode() = " + sheep.hashCode());
System.out.println("sheep1.hashCode() = " + sheep1.hashCode());
System.out.println("sheep2.hashCode() = " + sheep2.hashCode());
System.out.println("sheep3.hashCode() = " + sheep3.hashCode());
}
}
2.2 原型模式解决克隆羊问题

Java 中 Object 类是所有类的根类,Object 类提供了一个 clone()方法,该方法可以将一个 Java 对象复制
一份,但是需要实现 clone的Java类必须要实现一个接口Cloneable,该接口表示该类能够复制且具有复制的能力 — 原型模式

第一步:Sheep实体类实现Cloneable接口
第二步:Sheep中重写clone()方法
1
2
3
4
5
6
7
8
9
10
11
12
13
/**
*克隆该实例,使用默认的 clone 方法来完成
* */
@Override
protected Object clone() {
Sheep sheep = null;
try {
sheep = (Sheep) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return sheep;
}
第三步:使用clone()方法克隆Sheep对象
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
/**
* @Date 2020/6/11 11:19
* @Version 10.21
* @Author DuanChaojie
*/
public class Client {
public static void main(String[] args) {
Sheep sheep = new Sheep("MM", 3, "yellow");

// 使用原型模式
Sheep sheep4 = (Sheep)sheep.clone();
Sheep sheep5 = (Sheep)sheep.clone();

/**
* sheep4 = Sheep{name='MM', age=3, color='yellow'}
* sheep5 = Sheep{name='MM', age=3, color='yellow'}
*/
System.out.println("sheep4 = " + sheep4);
System.out.println("sheep5 = " + sheep5);

/**
* sheep4.hashCode() = 1163157884
* sheep5.hashCode() = 1956725890
*/
System.out.println("sheep4.hashCode() = " + sheep4.hashCode());
System.out.println("sheep5.hashCode() = " + sheep5.hashCode());
}
}

3. 深拷贝和浅拷贝

3.1 浅拷贝
  1. 对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象。
  2. 对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会进行
    引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成
    员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值。
  3. 前面我们克隆羊就是浅拷贝。
  4. 浅拷贝是使用默认的 clone()方法来实现 sheep = (Sheep) super.clone();
3.2 深拷贝
  1. 复制对象的所有基本数据类型的成员变量值
  2. 为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象,直到该对象可达的所有对象。也就是说, 对象进行深拷贝要对整个对象( 包括对象的引用类型) 进行拷贝。
  3. 深拷贝实现方式:
    1. 重写 clone 方法来实现深拷贝
    2. 通过对象序列化实现深拷贝(推荐)
准备工作

测试深拷贝,需要让DeepCloneTarget类作为DeepProtoType类的属性。

DeepProtoType类

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
/**
* @Date 2020/6/11 15:55
* @Version 10.21
* @Author DuanChaojie
*/
public class DeepProtoType implements Serializable, Cloneable{

// String属性
public String name;
// 引用类型
public DeepCloneTarget deepCloneTarget;

public DeepProtoType() {
super();
}

@Override
public String toString() {
return "DeepProtoType{" +
"name='" + name + '\'' +
", deepCloneTarget=" + deepCloneTarget +
'}';
}

}

DeepProtoType类

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
/**
* @Date 2020/6/11 15:55
* @Version 10.21
* @Author DuanChaojie
*/
public class DeepCloneTarget implements Serializable, Cloneable {

private String cloneName;

private Integer cloneAge;

/**
* 构造器
*/
public DeepCloneTarget(String cloneName, Integer cloneAge) {
this.cloneName = cloneName;
this.cloneAge = cloneAge;
}

/**
* 因为该类的属性,是String和Integer
* 因此我们这里使用默认的clone完成即可,即通过浅拷贝拷贝对象
* @return
* @throws CloneNotSupportedException
*/
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}

@Override
public String toString() {
return "DeepCloneTarget{" +
"cloneName='" + cloneName + '\'' +
", cloneAge=" + cloneAge +
'}';
}
}
方案一:重写 clone 方法实现深拷贝

在DeepProtoType类中重写clone()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//深拷贝 - 方式 1 使用clone 方法
@Override
protected Object clone() throws CloneNotSupportedException {

Object deep = null;

//这里完成对基本数据类型(属性)和String的克隆
deep = super.clone();

//对引用类型的属性,进行单独处理
DeepProtoType deepProtoType = (DeepProtoType) deep;

deepProtoType.deepCloneTarget = (DeepCloneTarget) deepCloneTarget.clone();

return deepProtoType;
}

测试结果

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
/**
* @Date 2020/6/11 16:07
* @Version 10.21
* @Author DuanChaojie
*/
public class client {
public static void main(String[] args) throws CloneNotSupportedException {
DeepProtoType dpt = new DeepProtoType();
dpt.name = "MM";
dpt.deepCloneTarget = new DeepCloneTarget("DD",17);

/**
* dpt = DeepProtoType{name='MM', deepCloneTarget=DeepCloneTarget{cloneName='DD', cloneAge=17}}
* dpt.deepCloneTarget.hashCode() = 1163157884
* dpt2 = DeepProtoType{name='MM', deepCloneTarget=DeepCloneTarget{cloneName='DD', cloneAge=17}}
* dpt2.deepCloneTarget.hashCode() = 1956725890
*/
System.out.println("dpt = " + dpt);
System.out.println("dpt.deepCloneTarget.hashCode() = " + dpt.deepCloneTarget.hashCode());

DeepProtoType dpt2 = (DeepProtoType)dpt.clone();
System.out.println("dpt2 = " + dpt2);
System.out.println("dpt2.deepCloneTarget.hashCode() = " + dpt2.deepCloneTarget.hashCode());


}

}
方案二:对象序列化实现深拷贝(推荐)
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
/**
* 深拷贝 - 方式2 通过对象的序列化实现 (推荐)
* @return
*/
public Object deepClone(){

// 将流对象中的数组输入到内存中
ByteArrayInputStream bis = null;
// 序列化流
ObjectInputStream ois = null;
//将内存中的数组输出
ByteArrayOutputStream bos = null;
// 反序列化流
ObjectOutputStream oos = null;

try {
// 序列化
bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bos);

// 当前这个对象以对象流的方式输出
oos.writeObject(this);

// 反序列化
bis = new ByteArrayInputStream(bos.toByteArray());
ois = new ObjectInputStream(bis);

DeepProtoType copydeepProtoType = (DeepProtoType)ois.readObject();
return copydeepProtoType;
} catch (Exception e1) {
e1.printStackTrace();
return null;
}finally {
//关闭流
try {
bos.close();
oos.close();
bis.close();
ois.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
}
测试
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
/**
* @Date 2020/6/11 16:07
* @Version 10.21
* @Author DuanChaojie
*/
public class client {
public static void main(String[] args) throws CloneNotSupportedException {
DeepProtoType dpt = new DeepProtoType();
dpt.name = "MM";
dpt.deepCloneTarget = new DeepCloneTarget("DD",17);

/**
* dpt = DeepProtoType{name='MM', deepCloneTarget=DeepCloneTarget{cloneName='DD', cloneAge=17}}
* dpt.deepCloneTarget.hashCode() = 1163157884
* dpt2 = DeepProtoType{name='MM', deepCloneTarget=DeepCloneTarget{cloneName='DD', cloneAge=17}}
* dpt2.deepCloneTarget.hashCode() = 1956725890
*/
System.out.println("dpt = " + dpt);
System.out.println("dpt.deepCloneTarget.hashCode() = " + dpt.deepCloneTarget.hashCode());

DeepProtoType dpt2 = (DeepProtoType)dpt.clone();
System.out.println("dpt2 = " + dpt2);
System.out.println("dpt2.deepCloneTarget.hashCode() = " + dpt2.deepCloneTarget.hashCode());

//方式 2 完成深拷贝
DeepProtoType dpt3 = (DeepProtoType) dpt.deepClone();
/**
* dpt.name=MM dpt.deepCloneableTarget=1163157884
* dpt3.name=MM dpt3.deepCloneableTarget=189568618
*/
System.out.println("dpt.name=" + dpt.name + " dpt.deepCloneableTarget=" + dpt.deepCloneTarget.hashCode());
System.out.println("dpt3.name=" + dpt3.name + " dpt3.deepCloneableTarget=" + dpt3.deepCloneTarget.hashCode());

}

}

4. 小结

  1. 创建新的对象比较复杂时,可以利用原型模式简化对象的创建过程,同时也能够提高效率。
  2. 不用重新初始化对象,而是动态地获得对象运行时的状态
  3. 如果原始对象发生变化(增加或者减少属性--这个指的是对被克隆的对象,如果修改被克隆对象里面的对象属性则需要通过深克隆才能克隆其对象属性),其它克隆对象的也会发生相应的变化,无需修改代码。
  4. 在实现深克隆的时候可能需要比较复杂的代码,实现深克隆的缺点:需要为每一个类配备一个克隆方法,这对全新的类来说不是很难,但对已有的类进行改造时,需要修改其源代码,违背了 ocp 原则。