不可变的对象指的是一旦创建之后,它的状态就不能改变。String类就是个不可变类,它的对象一旦创建之后,值就不能被改变了。
阅读更多: 为什么String类是不可变的
不可变对象对于缓存是非常好的选择,因为你不需要担心它的值会被更改。不可变类的另外一个好处是它自身是线程安全的,你不需要考虑多线程环境下的线程安全问题。
阅读更多: Java线程教程以及Java多线程面试问题
下面是创建不可变类的方法,我也给出了代码,加深理解。
要创建不可变类,要实现下面几个步骤:
- 将类声明为final,所以它不能被继承
- 将所有的成员声明为私有的,这样就不允许直接访问这些成员
- 对变量不要提供setter方法
- 将所有可变的成员声明为final,这样只能对它们赋值一次
- 通过构造器初始化所有成员,进行深拷贝(deep copy)
- 在getter方法中,不要直接返回对象本身,而是克隆对象,并返回对象的拷贝
为了理解第5和第6条,我将使用FinalClassExample来阐明。
FinalClassExample.java
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
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
|
package
com.journaldev.java;
import
java.util.HashMap;
import
java.util.Iterator;
public
final
class
FinalClassExample {
private
final
int
id;
private
final
String name;
private
final
HashMap testMap;
public
int
getId() {
return
id;
}
public
String getName() {
return
name;
}
/**
* 可变对象的访问方法
*/
public
HashMap getTestMap() {
//return testMap;
return
(HashMap) testMap.clone();
}
/**
* 实现深拷贝(deep copy)的构造器
* @param i
* @param n
* @param hm
*/
public
FinalClassExample(
int
i, String n, HashMap hm){
System.out.println(
"Performing Deep Copy for Object initialization"
);
this
.id=i;
this
.name=n;
HashMap tempMap=
new
HashMap();
String key;
Iterator it = hm.keySet().iterator();
while
(it.hasNext()){
key=it.next();
tempMap.put(key, hm.get(key));
}
this
.testMap=tempMap;
}
/**
* 实现浅拷贝(shallow copy)的构造器
* @param i
* @param n
* @param hm
*/
/**
public FinalClassExample(int i, String n, HashMap hm){
System.out.println("Performing Shallow Copy for Object initialization");
this.id=i;
this.name=n;
this.testMap=hm;
}
*/
/**
* 测试浅拷贝的结果
* 为了创建不可变类,要使用深拷贝
* @param args
*/
public
static
void
main(String[] args) {
HashMap h1 =
new
HashMap();
h1.put(
"1"
,
"first"
);
h1.put(
"2"
,
"second"
);
String s =
"original"
;
int
i=
10
;
FinalClassExample ce =
new
FinalClassExample(i,s,h1);
//Lets see whether its copy by field or reference
System.out.println(s==ce.getName());
System.out.println(h1 == ce.getTestMap());
//print the ce values
System.out.println(
"ce id:"
+ce.getId());
System.out.println(
"ce name:"
+ce.getName());
System.out.println(
"ce testMap:"
+ce.getTestMap());
//change the local variable values
i=
20
;
s=
"modified"
;
h1.put(
"3"
,
"third"
);
//print the values again
System.out.println(
"ce id after local variable change:"
+ce.getId());
System.out.println(
"ce name after local variable change:"
+ce.getName());
System.out.println(
"ce testMap after local variable change:"
+ce.getTestMap());
HashMap hmTest = ce.getTestMap();
hmTest.put(
"4"
,
"new"
);
System.out.println(
"ce testMap after changing variable from accessor methods:"
+ce.getTestMap());
}
}
|
输出:
1
2
3
4
5
6
7
8
9
10
|
Performing Deep Copy for Object initialization
true
false
ce id:10
ce name:original
ce testMap:{2=second, 1=first}
ce id after local variable change:10
ce name after local variable change:original
ce testMap after local variable change:{2=second, 1=first}
ce testMap after changing variable from accessor methods:{2=second, 1=first}
|
现在我们注释掉深拷贝的构造器,取消对浅拷贝构造器的注释。也对getTestMap()方法中的返回语句取消注释,返回实际的对象引用。然后再一次执行代码。
1
2
3
4
5
6
7
8
9
10
|
Performing Shallow Copy for Object initialization
true
true
ce id:10
ce name:original
ce testMap:{2=second, 1=first}
ce id after local variable change:10
ce name after local variable change:original
ce testMap after local variable change:{3=third, 2=second, 1=first}
ce testMap after changing variable from accessor methods:{3=third, 2=second, 1=first, 4=new}
|
从输出可以看出,HashMap的值被更改了,因为构造器实现的是浅拷贝,而且在getter方法中返回的是原本对象的引用。