原型模式

需求引入

有一只羊,姓名:duoli,年龄:1, 请编写程序创建和duoli属性完全一样的10只羊出来

使用传统方式

思路

image-220190826114911487

代码

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
public class Sheep {
private String name;
private int age;

public Sheep() {
}

public Sheep(String name, int age) {
this.name = name;
this.age = 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;
}
}

Client

1
2
3
4
5
6
7
public class Client {
public static void main(String[] args) {
Sheep sheep = new Sheep("duoli",1);
Sheep sheep2 = new Sheep(sheep.getName(),sheep.getAge());
Sheep sheep3 = new Sheep(sheep.getName(),sheep.getAge());
}
}

优缺点

  • 优点比较好理解,简单易操作
  • 新建对象时,总是需要重新获取原始对象的属性,如果创建对象比较复杂时候,效率比较低。
  • 总是需要重新初始化对象,而不是动态获取运行时的状态,不够灵活

改进思路

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

基本介绍

  • 原型模式(Prototype Pattern):用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象。
  • 原型模式属于创建型设计模式,允许一个对象再创建另外一个可定制的对象,无需知道如何创建的细节。
  • 工作原理:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建,即对象.clone();
  • 原型模式中包含如下要素:
    • Prototype: 原型类,声明一个克隆自己的接口。
    • ConcreteProtoype:具体的圆形类,实现克隆自己的操作。
    • Client:让一个克隆对象克隆自己,从而创建一个新的对象(属性一样)。

使用原型模式

思路

  • sheep类实现Cloneable重写clone方法。

代码

Sheep2

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
public class Sheep2 implements Cloneable{
private String name;
private int age;
/**
* 对象类型,克隆是浅拷贝
*/
private Sheep friend;

public Sheep2() {
}

public Sheep2(String name, int age) {
this.name = name;
this.age = 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 Sheep getFriend() {
return friend;
}

public void setFriend(Sheep friend) {
this.friend = friend;
}

@Override
protected Sheep2 clone() {
Sheep2 sheep2 = null;
try{
sheep2 = (Sheep2) super.clone();
}catch (Exception e){
e.printStackTrace();
}
return sheep2;
}

@Override
public String toString() {
return "name:"+name + "age:"+age;
}
}

Client

1
2
3
4
5
6
7
8
9
10
11
public class Client {
public static void main(String[] args) {
Sheep2 sheep = new Sheep2("duoli",1);
Sheep friend = new Sheep("duoli 的朋友",2);
sheep.setFriend(friend);
Sheep2 clone = sheep.clone();
Sheep2 clone2 = sheep.clone();
//判断为true, 对象类型,克隆只拷贝地址引用
System.out.println((clone.getFriend() == clone2.getFriend()));
}
}

浅拷贝和深拷贝

  • “==”和 equals 方法究竟有什么区别?

    • ==操作符专门用来比较两个变量的值是否相等,也就是用于比较变量所对应的内存中所存储的数值是否相同,要比较两个基本类型的数据或两个引用变量是否相等,只能用==操作符。

    • 如果一个变量指向的数据是对象类型的,那么,这时候涉及了两块内存,对象本身占用一块内存(堆

      内存),变量也占用一块内存,例如 Objet obj = new Object();变量 obj 是一个内存,内存存放的是地址引用(指向new Object开辟的内存地址),new Object()是另一个内存,此时,变量 obj 所对应的内存中存储的数值就是对象占用的那块内存的首地址。对于指向对象类型的变量,如果要比较两个变量是否指向同一个对象,即看这两个变量所对应的内存中的数值是否相等,这时候就需要用==操作符进行比较。

    • equals 方法是用于比较两个独立对象的内容是否相同,就好比去比较两个人的长相是否相同,它比较的两个对象是独立的。

      1
      2
      3
      String a=new String("foo");
      String b=new String("foo");
      //两条 new 语句创建了两个对象,然后用 a,b 这两个变量分别指向了其中一个对象,这是两个不同的对象,它们的首地址是不同的,即 a 和 b 中存储的数值是不相同的,所以,表达式 a==b 将返回 false,而这两个对象中的内容是相同的,所以,表达式 a.equals(b)将返回 true。

浅拷贝

  • 对于数据类型是基础数据类型的成员变量,浅拷贝会直接进行值传递,也就是该属性值复制一份给新的对象。
  • 对于数据类型是应用数据类型的成员变量,比如说成员是某个数组、某个类的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指向了同一个实例,这种情况下,再一个对象修改成员变量会影响到另一个对象的成员变量值。
  • Object.clone()方法 是浅拷贝。

深拷贝

  • 复制对象的所有基本类型的成员变量值
  • 为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所有引用的对象,直到该对象可达的所有对象。也就是说,对象进行深拷贝要多整个对象(包括对应的引用类型)进行拷贝
  • 实现深拷贝方式1:重写clone方法实现深拷贝
  • 实现深拷贝方式2:通过对象序列化实现深拷贝(推荐)

重写clone方法实现深拷贝

DeepCloneableTarget

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
public class DeepCloneableTarget implements Cloneable , Serializable {
private static final long serialVersionUID = 1L;
private String cloneName;
private String cloneClass;

public DeepCloneableTarget(String cloneName, String cloneClass) {
this.cloneName = cloneName;
this.cloneClass = cloneClass;
}

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

public String getCloneName() {
return cloneName;
}

public void setCloneName(String cloneName) {
this.cloneName = cloneName;
}

public String getCloneClass() {
return cloneClass;
}

public void setCloneClass(String cloneClass) {
this.cloneClass = cloneClass;
}
}

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
public class DeepProtoType  implements Cloneable , Serializable {
private static final long serialVersionUID = 1L;
private String name;
private DeepCloneableTarget deepCloneableTarget;

public String getName() {
return name;
}

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

public DeepCloneableTarget getDeepCloneableTarget() {
return deepCloneableTarget;
}

public void setDeepCloneableTarget(DeepCloneableTarget deepCloneableTarget) {
this.deepCloneableTarget = deepCloneableTarget;
}

@Override
protected DeepProtoType clone() throws CloneNotSupportedException {
DeepProtoType deepProtoType = null;
deepProtoType = (DeepProtoType)super.clone();
deepProtoType.deepCloneableTarget = (DeepCloneableTarget)deepProtoType.getDeepCloneableTarget().clone();
return deepProtoType;
}
}

Client

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Client {
public static void main(String[] args) {
try{
DeepProtoType protoType = new DeepProtoType();
protoType.setName("A");
protoType.setDeepCloneableTarget(new DeepCloneableTarget("B","C"));
DeepProtoType clone = protoType.clone();
DeepProtoType clone2 = protoType.clone();
//判断为true, 对象类型,克隆只拷贝地址引用
System.out.println((clone.getDeepCloneableTarget() == clone2.getDeepCloneableTarget()));
}catch (Exception e){
e.printStackTrace();
}
}
}

通过对象序列化实现深拷贝

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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
public class DeepProtoType Serializable {
private static final long serialVersionUID = 1L;
private String name;
private DeepCloneableTarget deepCloneableTarget;

public String getName() {
return name;
}

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

public DeepCloneableTarget getDeepCloneableTarget() {
return deepCloneableTarget;
}

public void setDeepCloneableTarget(DeepCloneableTarget deepCloneableTarget) {
this.deepCloneableTarget = deepCloneableTarget;
}

public Object deepClone(){
ByteArrayOutputStream bos = null;
ObjectOutputStream oos = null;
ByteArrayInputStream bis = null;
ObjectInputStream ois = null;
try{
//序列化
bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bos);
//当前对象以对象流的方式输出
oos.writeObject(this);

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

DeepProtoType deepProtoType = (DeepProtoType)ois.readObject();
return deepProtoType;
}catch (Exception e){
e.printStackTrace();
}finally {
try{
bos.close();
oos.close();
bis.close();
ois.close();
}catch (Exception e){
e.printStackTrace();
}
}
return null;
}
}

Client

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Client {
public static void main(String[] args) {
try{
DeepProtoType protoType = new DeepProtoType();
protoType.setName("A");
protoType.setDeepCloneableTarget(new DeepCloneableTarget("B","C"));
DeepProtoType clone = (DeepProtoType)protoType.deepClone();
DeepProtoType clone2 = (DeepProtoType)protoType.deepClone();
//判断为true, 对象类型,克隆只拷贝地址引用
System.out.println((clone.getDeepCloneableTarget() == clone2.getDeepCloneableTarget()));
}catch (Exception e){
e.printStackTrace();
}
}
}