..自动线程安全并且没有同步问题
当两个不同的线程修改同一个对象的状态时,就会出现并发问题。不可变对象不能被修改,所以没有问题。
示例:A String
。两个线程可以String
毫无顾虑地传递相同的线程,因为它们都不能以任何方式改变它。
不需要复制构造函数
...因为复制是改变它的唯一方法。不可变对象的一种常见设计模式,用于每个“修改”操作以制作副本,然后对新对象执行操作。
复制构造函数通常用于您想要更改而不影响原始对象的对象。对于不可变对象,情况总是如此(根据定义)。
在 的情况下String
,所有方法和+
运算符都返回 new String
。
不需要克隆的实现
看上面。
用作场时不需要防守复制
曾几何时,我做了一件愚蠢的事。我在列表中有一组枚举:
private static final List<Status> validStatuses;
static {
validStatuses = new ArrayList<Status>();
validStates.add(Status.OPEN);
validStates.add(Status.REOPENED);
validStates.add(Status.CLOSED);
}
这个列表是从一个方法返回的:
public static List<Status> getAllStatuses() {
return validStates;
}
我检索了该列表,但只想在界面中显示打开状态:
List<Status> statuses = Status.getAllStatuses();
statuses.remove(Status.CLOSED);
太好了,它奏效了!等等,现在所有状态列表都只显示这两个——即使在页面刷新之后!发生了什么?我修改了一个静态对象。哎呀。
我本可以对getAllStatuses
. 或者,我可以首先使用Guava 的 ImmutableList之类的东西:
private static final List<Status> validStatuses =
ImmutableList.of(Status.OPEN, Status.REOPENED, Status.CLOSED);
然后当我做了一些愚蠢的事情时:
List<Status> statuses = Status.getAllStatuses();
statuses.remove(Status.CLOSED); // Exception!
总是具有“故障原子性”(Joshua Bloch 使用的术语):如果不可变对象抛出异常,它永远不会处于不希望或不确定的状态。
因为类永远不能被修改,所以修改发出的所有状态都是完整的、合格的对象(因为它们不能改变,它们必须始终处于合格状态才能有用)。异常不会发出新对象,因此您永远不会有不良或不确定的状态。