编程中的不可变性

今天突然想起了一道面试题,简析不可变性 在编程中的优势?

优势

  • 并发安全
  • 函数式编程中大量利用了不可变数据结构
  • 性能(不好说,好像略低于原始结构),但是可以利用缓存
  • 不会被黑客攻击,比如String
  • 省内存,比如String Intern机制
  • 可以避免NULL引用

如何写一个不可变类

  1. 将类声明为final,所以它不能被继承
  2. 将所有的成员声明为私有的,这样就不允许直接访问这些成员
  3. 对变量不要提供setter方法
  4. 将所有可变的成员声明为final,这样只能对它们赋值一次
  5. 通过构造器初始化所有成员,进行深拷贝(deep copy)
  6. 在getter方法中,不要直接返回对象本身,而是克隆对象,并返回对象的拷贝

一个Demo

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
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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
package cb;

/**
* @author 宋建涛 E-mail:1872963677@qq.com
* @version 创建时间:2019年6月17日 下午2:27:51
* 类说明
*/

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=(String) 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());

}

}



### package cb;
/**

* @author 宋建涛 E-mail:1872963677@qq.com
* @version 创建时间:2019年6月17日 下午2:27:51
* 类说明
*/

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=(String) 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());

}

}

也可以使用建造者模式创建不可变类

当成员变量非常多时,适合用这种方法
http://www.importnew.com/7860.html
比如在这篇文章中,创建不可变的Map

String源码解析

https://juejin.im/post/59fffddc5188253d6816f9c1

关于String的各种问题

https://blog.csdn.net/qq_34490018/article/details/82110578

为什么StringBuffer和StringBuilder就可变?

因为成员变量value数组没有被final修饰,所以可以修改他的引用变量的值,即可以引用到新的数组对象。所以StringBuilder对象是可变的

参考

http://www.importnew.com/7535.html
http://www.importnew.com/14027.html

-------------本文结束感谢您的阅读-------------