大话深入浅出Effective Java核心实战编程思想之——猴王的把戏
不知道大家还记不记得在《西游记》里的莲花洞夺宝的故事,就是猴王巧夺宝物,收复金银角大王那一章。到底这个故事给了我们什么启示呢?这故事又和Effective Java有什么联系?还是延续上篇文章的风格吧,看代码,读故事。
看到这里你应该对深克隆和浅克隆有了初步的了解了吧?现在我们再看怎样深克隆一个猴王,哦,不对,应该是真正猴王的七十二变。(为什么我叫他猴王,因为孙悟空有歧义)。
程序员的一生其实可短暂了,这电脑一开一关,一天过去了,嚎;电脑一开不关,那就成服务器了,嚎……
1
import
static
org.junit.Assert.
*
;
2
import
org.junit.Test;
3![]()
4![]()
5![]()
public
class
TestClone
{
6
7
@Test
8![]()
public void testClone()
{
9
// 西天取经的路上,金角大王和银角大王把唐僧抓走了
10
猴王 齐天大圣=new 猴王("齐天大圣孙悟空");
11
//大圣手拿金箍棒,正要收拾金、银角大王。
12
齐天大圣.取得武器(new 金箍棒());
13
14![]()
/**//*
15
* 这时候,金角大王和银角大王听闻大圣来者不善,立马让小妖去请出他们的宝葫芦
16
* 当然这一切瞒不过神通广大的大圣爷。大圣猴毛一吹,变出一个老道士。
17
*/
18
猴王 空悟孙道士=(猴王)齐天大圣.变出一个化身();
19
空悟孙道士.改名("空悟孙道士");
20
21![]()
/**//*
22
* 老道士忽悠小妖说他的葫芦更厉害,能把天都给收了,智力值只有20的小妖看了羡慕不已,要求交换葫芦。
23
* 老道士自然很乐意,换了葫芦,直奔妖怪洞穴,收服了金、银角大王。
24
*/
25
空悟孙道士.取得武器(new 宝葫芦());
26
27
//问题1:道士拿的是什么武器?道士是由大圣克隆而来,拿的却不是金箍棒,而是宝葫芦?
28
assertFalse(齐天大圣.的武器() instanceof 金箍棒);
29
assertTrue(空悟孙道士.的武器() instanceof 宝葫芦);
30
31
//问题2:大圣和道士拿同一个武器?
32
assertSame(空悟孙道士.的武器(),齐天大圣.的武器());
33
34
//问题3:既然武器是一样的,为什么名字又不一样呢?
35
assertEquals(齐天大圣.名字(),"齐天大圣孙悟空");
36
assertEquals(空悟孙道士.名字(),"空悟孙道士");
37
38![]()
/**//*
39
* 答案:猴王类继承了Object.clone(),其克隆原理是:如果类每个域包含一个原语类型(primitive)的值,
40
* 或者包含一个指向非可变(final)对象的引用,那么返回的值或对象是一个相同的拷贝;否则,如果是可变类,则会返回相同的引用。
41
* 因为金箍棒类不是非可变类,而String是,所以你应该明白,为什么大圣爷和他的克隆体有不同的名字,却有相同的武器吧。
42
*
43
* Object.clone()被称为浅拷贝,或浅克隆。相对应的是深克隆(deep clone),他是指类在克隆时也拷贝可变对象。
44
* 看到这里你应该知道其实这个猴王类实现得不合理,他应该拥有一个深克隆的方法。
45
*/
46
}
47![]()
48![]()
class 猴王 implements Cloneable
{
49
private String name;
50
private 武器[] weapon=new 武器[1];
51
52![]()
public 猴王(String name)
{
53
this.name=name;
54
}
55
56![]()
/** *//**
57
* 取得一个猴王的浅克隆化身
58
* @return
59
*/
60![]()
public Object 变出一个化身()
{
61
Object cloneObj=null;
62![]()
try
{
63
cloneObj=clone();
64![]()
}catch(CloneNotSupportedException ex)
{
65
ex.printStackTrace();
66
}
67
return cloneObj;
68
}
69
70
@Override
71![]()
protected Object clone() throws CloneNotSupportedException
{
72
return super.clone();
73
}
74
75![]()
public String 名字()
{
76
return name;
77
}
78
79![]()
public void 改名(String name)
{
80
this.name=name;
81
}
82![]()
83![]()
public 武器 的武器()
{
84
return weapon[0];
85
}
86![]()
87![]()
public void 取得武器(武器 weapon)
{
88
this.weapon[0] = weapon;
89
}
90
}
91
92![]()
class 武器
{
93![]()
public 武器()
{
94
95
}
96
}
97
98![]()
class 金箍棒 extends 武器
{
99![]()
public 金箍棒()
{
100
}
101
}
102
103![]()
class 宝葫芦 extends 武器
{
104![]()
public 宝葫芦()
{
105
}
106
}
107
108![]()
109
}
110
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

80
81
82
83

84
85
86
87

88
89
90
91
92

93

94
95
96
97
98

99

100
101
102
103

104

105
106
107
108
109
110
看到这里你应该对深克隆和浅克隆有了初步的了解了吧?现在我们再看怎样深克隆一个猴王,哦,不对,应该是真正猴王的七十二变。(为什么我叫他猴王,因为孙悟空有歧义)。
1
import
static
org.junit.Assert.assertEquals;
2
import
static
org.junit.Assert.assertFalse;
3
import
static
org.junit.Assert.assertNotSame;
4
import
static
org.junit.Assert.assertTrue;
5![]()
6
import
org.junit.Test;
7![]()
8![]()
9![]()
public
class
TestDeepClone
{
10
11
@Test
12![]()
public void testDeepClone()
{
13
// 西天取经的路上,金角大王和银角大王把唐僧抓走了
14
猴王 齐天大圣=new 猴王("齐天大圣孙悟空");
15
//大圣手拿金箍棒,正要收拾金、银角大王。
16
齐天大圣.取得武器(new 金箍棒());
17
18![]()
/**//*
19
* 这时候,金角大王和银角大王听闻大圣来者不善,立马让小妖去请出他们的宝葫芦
20
* 当然这一切瞒不过神通广大的大圣爷。大圣猴毛一吹,变出一个老道士。
21
*/
22
猴王 空悟孙道士=(猴王)齐天大圣.变出一个化身();
23
空悟孙道士.改名("空悟孙道士");
24
25![]()
/**//*
26
* 老道士忽悠小妖说他的葫芦更厉害,能把天都给收了,智力值只有20的小妖看了羡慕不已,要求交换葫芦。
27
* 老道士自然很乐意,换了葫芦,直奔妖怪洞穴,收服了金、银角大王。
28
*/
29
齐天大圣.取得武器(new 宝葫芦());
30
31
32
assertTrue(空悟孙道士.的武器() instanceof 金箍棒);
33
assertFalse(空悟孙道士.的武器() instanceof 宝葫芦);
34
assertNotSame(空悟孙道士.的武器(),齐天大圣.的武器());
35
assertEquals(齐天大圣.名字(),"齐天大圣孙悟空");
36
assertEquals(空悟孙道士.名字(),"空悟孙道士");
37
}
38
39![]()
class 猴王 implements Cloneable
{
40
private String name;
41
private 武器 weapon;
42
43![]()
public 猴王(String name)
{
44
this.name=name;
45
}
46
47![]()
/** *//**
48
* 取得一个猴王的浅克隆化身
49
* @return
50
*/
51![]()
public Object 变出一个化身()
{
52
Object cloneObj=null;
53![]()
try
{
54
cloneObj=clone();
55![]()
}catch(CloneNotSupportedException ex)
{
56
ex.printStackTrace();
57
}
58
return cloneObj;
59
}
60
61![]()
/** *//**
62
* 取得一个猴王的深克隆化身
63
* @return
64
*/
65![]()
public Object 变出一个新化身()
{
66
Object cloneObj=null;
67![]()
try
{
68
cloneObj=clone();
69![]()
}catch(CloneNotSupportedException ex)
{
70
ex.printStackTrace();
71
}
72
return cloneObj;
73
}
74
75
@Override
76![]()
protected Object clone() throws CloneNotSupportedException
{
77
return super.clone();
78
}
79
80![]()
public String 名字()
{
81
return name;
82
}
83
84![]()
public void 改名(String name)
{
85
this.name=name;
86
}
87![]()
88![]()
public 武器 的武器()
{
89
return weapon;
90
}
91![]()
92![]()
public void 取得武器(武器 weapon)
{
93
this.weapon = weapon;
94
}
95
}
96
97![]()
abstract class 武器 implements Cloneable
{
98![]()
public 武器()
{
99
100
}
101
102
@Override
103![]()
public Object clone()
{
104
Object result=null;
105![]()
try
{
106
result= super.clone();
107![]()
}catch(CloneNotSupportedException ex)
{
108
ex.printStackTrace();
109
}
110
return result;
111
}
112
}
113
114![]()
class 金箍棒 extends 武器
{
115![]()
public 金箍棒()
{
116
}
117
118
@Override
119![]()
public Object clone()
{
120
return super.clone();
121
}
122
}
123
124![]()
class 宝葫芦 extends 武器
{
125![]()
public 宝葫芦()
{
126
}
127
128
@Override
129![]()
public Object clone()
{
130
return super.clone();
131
}
132
}
133
}
134
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
80

81
82
83
84

85
86
87
88

89
90
91
92

93
94
95
96
97

98

99
100
101
102
103

104
105

106
107

108
109
110
111
112
113
114

115

116
117
118
119

120
121
122
123
124

125

126
127
128
129

130
131
132
133
134
程序员的一生其实可短暂了,这电脑一开一关,一天过去了,嚎;电脑一开不关,那就成服务器了,嚎……