原型模式

什么是原型模式

从一个对象再创建另外一个可定制对象,而且不需要知道创建的细节。
类似于僵尸母体,通过实现一个克隆的接口,将自身属性拷贝给其他僵尸,还可以进行基因突变。

拷贝方式分为

  • 浅拷贝:仅仅复制所考虑的对象,而不复制它所引用的对象
  • 深拷贝:将复制的对象引用的对象都复制一遍

浅拷贝

仅仅复制所考虑的对象,而不复制它所引用的对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Zombie{//一个僵尸类
public String id;
public Zombie cloneZombie() {//实现克隆方法
Zombie z = new Zombie();
z.id = this.id;//浅拷贝僵尸的id号
return z;
}
}
public class Main{
public static void main(String[] args){
Zombie cz1 = new Zombie();
cz1.id="001";
Zombie cz2 = cz1.cloneZombie();
System.out.println((cz1==cz2)+":"+cz1.id+","+cz2.id);//输出false:001,001
}

深拷贝

将复制的对象引用的对象都复制一遍

java中自带的java.lang.Object.clone()分析

在研究原型模式的时候,我将接口中的拷贝方法命名为clone(),却发现Java内部已经有了一个clone()方法。

1
2
3
public class Object {  
protected native Object clone() throws CloneNotSupportedException;
}

native修饰,表示是有JNI实现,效率远高于非native方法
protected修饰,表示要使用这个clone()必须要继承自Object类,Java中所有类都是继承自Object类。
返回Object对象,表示需要强制类型转换。

Java对象中的clone步骤

  1. 原型类实现Cloneable接口
  2. 重写clone方法,并调用super.clone()方法
  3. 实现深拷贝

原型类实现Cloneable接口

ctrl+左键查看源码发现,这是一个空的接口。

1
2
public interface Cloneable {
}

但是如果原型类没有实现Cloneable接口而去调用Object.clone()方法,会抛出CloneNotSupportedException异常

重写clone方法,并调用super.clone()方法

继承自java.lang.Object.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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class ZombieMSG {//僵尸信息类
public String id;
public String name;
public ZombieMSG(String id, String name) {
this.id = id;
this.name = name;
}
public String toString(){
return "(id:"+id+",name:"+name+")";
}
}
public class Zombie implements Cloneable{//僵尸类,实现Cloneable接口
public String id;
public ZombieMSG msg;
public Zombie(String id,String name){
this.id = id;
msg = new ZombieMSG(id, name);
}
public Object clone(){//重写clone方法
Object obj = null;
try {
obj = super.clone();//调用Object中的clone()方法
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return obj;
}
}
public class Main{
public static void main(String[] args){
Zombie pz = new Zombie("000","母体僵尸");
System.out.println(pz.id+":"+pz.msg);//输出000:(id:000,name:母体僵尸)

Zombie cz1 = (Zombie) pz.clone();
cz1.id="001";
cz1.msg.id="001";
cz1.msg.name="普通僵尸1号";
System.out.println((pz==cz1)+"-"+pz.id+":"+pz.msg+","+cz1.id+":"+cz1.msg);
//输出false-000:(id:001,name:普通僵尸1号),,001:(id:001,name:普通僵尸1号),说明是浅拷贝
}
}

为什么我们在派生类中覆盖Object的clone()方法时,一定要调用super.clone()呢?
在运行时刻,Object中的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
28
29
30
31
32
33
34
35
36
37
38
39
public class ZombieMSG implements Cloneable{//僵尸信息类,实现Cloneable接口
//其他代码不变,新增clone方法
public Object clone(){
ZombieMSG obj = null;
try {
obj = (ZombieMSG) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return obj;
}
}
public class Zombie implements Cloneable{//僵尸类,实现Cloneable接口
//其他代码不变,修改clone()方法
public Object clone(){
Zombie obj = null;
try {
obj = (Zombie) super.clone();
obj.msg = (ZombieMSG) this.msg.clone();//关键代码,将ZombieMSG也进行浅拷贝
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return obj;
}

}
public class Main{
public static void main(String[] args){
Zombie pz = new Zombie("000","母体僵尸");
System.out.println(pz.id+":"+pz.msg);//输出000:(id:000,name:母体僵尸)

Zombie cz1 = (Zombie) pz.clone();
cz1.id="001";
cz1.msg.id="001";
cz1.msg.name="普通僵尸1号";
System.out.println((pz==cz1)+"-"+pz.id+":"+pz.msg+","+cz1.id+":"+cz1.msg);
//输出false-000:(id:000,name:母体僵尸),001:(id:001,name:普通僵尸1号),说明是深拷贝
}
}