图解java多线程-Immutable_模式

正文

使用 Immutable 模式的要点

实例创建后,状态不再发生变化

如下定义一个 Persion 类,注意 class 为 final,表示不能被继承,并且类的属性也是不可变的

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
public final class Persion {

private final String name;
private final String address;

public Persion(String name, String address) {
this.name = name;
this.address = address;
}

public String getName() {
return name;
}

public String getAddress() {
return address;
}

@Override
public String toString() {
return "Persion{" +
"name='" + name + '\'' +
", address='" + address + '\'' +
'}';
}
}

比如下面这个类就不是不可变的,原因在于 Point 不是不可变的,因为外部可能还保存 Point 的引用

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
public final class Line {

private final Point startPoint;
private final Point endPoint;

public Line(Point startPoint, Point endPoint) {
this.startPoint = startPoint;
this.endPoint = endPoint;
}

public Point getStartPoint() {
return startPoint;
}

public Point getEndPoint() {
return endPoint;
}

@Override
public String toString() {
return "Line{" +
"startPoint=" + startPoint +
", endPoint=" + endPoint +
'}';
}

public static void main(String[] args) {
Point startPoint = new Point(1,1);
Point endPoint = new Point(2,2);
Line line = new Line(startPoint,endPoint);
System.out.println(line);
startPoint.x = 3;
startPoint.y = 3;
System.out.println(line);
}

static final class Point{
private int x;
private int y;

public Point(int x, int y) {
this.x = x;
this.y = y;
}

@Override
public String toString() {
return "Point{" +
"x=" + x +
", y=" + y +
'}';
}
}
}

运行结果:
Line{startPoint=Point{x=1, y=1}, endPoint=Point{x=2, y=2}}
Line{startPoint=Point{x=3, y=3}, endPoint=Point{x=2, y=2}}

上面的类可以将 Point 类改为不可变的,也可以改一下 Line 的构造方法,让外部不再持有引用,但是这样反射可能还是可以修改,一般这种类在程序中表示常量,而可以用枚举

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
public final class Line1 {

private final Point startPoint;
private final Point endPoint;

public Line1(Point startPoint, Point endPoint) {
this.startPoint = new Point(startPoint.x,startPoint.y);
this.endPoint = new Point(endPoint.x,endPoint.y);
}

public Point getStartPoint() {
return startPoint;
}

public Point getEndPoint() {
return endPoint;
}

@Override
public String toString() {
return "Line1{" +
"startPoint=" + startPoint +
", endPoint=" + endPoint +
'}';
}

public static void main(String[] args) {
Point startPoint = new Point(1,1);
Point endPoint = new Point(2,2);
Line1 line = new Line1(startPoint,endPoint);
System.out.println(line);
startPoint.x = 3;
startPoint.y = 3;
System.out.println(line);
try {
Field f = line.getClass().getDeclaredField("startPoint");
Object obj = f.get(line);
Field fx = obj.getClass().getDeclaredField("x");
fx.setAccessible(true);
Field fy = obj.getClass().getDeclaredField("y");
fy.setAccessible(true);
fx.set(obj,3);
fy.set(obj,3);
fx.setAccessible(false);
fy.setAccessible(false);
System.out.println(line);
} catch (Exception e) {
e.printStackTrace();
}
}

static final class Point{
private int x;
private int y;

public Point(int x, int y) {
this.x = x;
this.y = y;
}

@Override
public String toString() {
return "Point{" +
"x=" + x +
", y=" + y +
'}';
}
}
}

运行结果:
Line1{startPoint=Point{x=1, y=1}, endPoint=Point{x=2, y=2}}
Line1{startPoint=Point{x=1, y=1}, endPoint=Point{x=2, y=2}}
Line1{startPoint=Point{x=3, y=3}, endPoint=Point{x=2, y=2}}

实例是共享的,且被频繁访问时

Immutable 模式的优点是"不需要使用synchronized进行保护".能够在不失去安全性和生存性的前提下提高性能

考虑成对的 mutable 类和 immutable 类

比如jdk中的 StringBuffer 和 String 类. StringBuffer 类表示字符串的 mutable 类.它表示字符串可以随便改写,为了确保安全,改写时需要妥善使用 synchronized.而 String 类表示字符串的 immutable 类
StringBuffer 类中有一个以 String 为参数的构造函数,而 String 类中也有一个以 StringBuffer 为参数的构造函数.也就是说, StringBuffer 和 String 实例可以互相转换

参考资料

Cream Bing wechat
subscribe to my blog by scanning my public wechat account