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