Java不可变对象
我正在学习不变性的概念。
我知道一旦创建了对象,不可变对象就无法更改它们的值。
但我不明白以下对不可变对象的使用。
他们是
- 是自动线程安全的,没有同步问题。
How ? Proof ?
- 不需要复制构造函数。
How ? Any example ?
- 不需要克隆的实现
How ? Any example ?
How ? Any example ?
- 用作场时不需要防守复制
How ? Any example ?
How ? Any example ?
- 总是有
"failure atomicity" (a term used by Joshua Bloch)
:如果一个不可变对象抛出exception,它就永远不会处于不受欢迎或不确定的状态。How ? Any example ?
有人可以通过支持它的例子详细解释这些要点吗?
谢谢。
..自动线程安全,没有同步问题
当两个不同的线程修改同一对象的状态时,会发生并发问题。 不可变对象无法修改,因此没有问题。
示例: String
。 两个线程可以毫无顾虑地传递给同一个String
,因为它们都不能以任何方式改变它。
不需要复制构造函数
…因为副本是改变它的唯一方法。 每个“修改”操作的不可变对象的一种常见设计模式,用于复制然后对新对象执行操作。
复制构造函数通常用于要更改的对象,而不会影响原始对象。 对于不可变对象,总是如此(根据定义)。
在String
的情况下,所有方法和+
运算符都返回新的String
。
不需要克隆的实现
往上看。
当用作场时,不需要防御性地复制
曾几何时我做了些傻事。 我在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!
总是有“失败primefaces性”(Joshua Bloch使用的术语):如果一个不可变对象抛出exception,它就永远不会处于不受欢迎或不确定的状态。
因为永远不能修改类,所以通过修改发出的所有状态都是完整的限定对象(因为它们不能更改,所以它们必须始终处于合格状态才有用)。 exception不会发出新对象,因此您永远不会有不受欢迎或不确定的状态。
它们是自动线程安全的,没有同步问题
是的,因为Java Memory Model为最终字段提供了保证:
final字段还允许程序员在没有同步的情况下实现线程安全的不可变对象。 线程安全的不可变对象被所有线程视为不可变对象,即使使用数据争用传递线程之间的不可变对象的引用也是如此。
用作场时不需要防守复制如何? 任何例子?
因为它们是不可变的,所以它们不能被修改,所以可以与外部代码共享它们(你知道它们无法弄乱对象的状态)。
推论:您不需要复制/克隆不可变对象。
总是有“失败primefaces性”
一旦正确构造,不可变对象就不会改变。 因此,无论是构造失败还是exception,或者它都没有,并且您知道对象处于一致状态。
这不是一个可以通过它的例子有用地解释的概念。 不可变对象的优点是你知道他们的数据不能改变,所以你不必担心这一点。 您可以自由使用不可变对象,而不必担心传递它们的方法会改变它。
当我们执行multithreading程序比这更方便时,因为基于线程更改的数据的错误不应该被完成
自动线程安全
- 因为它们无法更改(无法变异) – 任何访问它的线程都会找到处于相同状态的对象。 所以没有像一个线程改变对象状态的情况,然后第二个线程接管并改变对象的状态,然后再次第一个接管而没有任何线索,它被别人改变了
- 好的例子是ArrayList – 如果一个线程遍历其’元素并且第二个线程删除其中的一些,则第一个线程然后抛出某种并发exception。 使用不可变列表可以防止这种情况
复制构造函数
- 它并不意味着它不能拥有复制构造函数。 它是一个构造函数,您传递相同类型的对象,并创建新对象作为给定对象的副本。 这只是猜测,但为什么要复制始终处于同一状态的对象?
public class A { private int a; public A(int a) { this.a = a; } public A(A original) { this.a = original.a; } }
克隆的实现
- 同样的问题,克隆对象,总是在同一状态,通常只占用内存空间。 但是你可以这样做,如果你想用不可变的方法创建可变对象
- 好的例子是再次集合,你可以生成不可变的可变集合
防御性复制
- 防御性复制意味着,当您将对象设置为字段时,您将创建相同类型的新对象,即原始对象的副本
- 例
- Tomcat 6内存泄漏日志条目
- 队列没有自然排序
- Spring引导静态资源使用ResourceHandlerRegistry映射ant匹配器
- 在jenkins构建下以无头模式运行cucumber-jvm selenium测试用例时出错
- 如何使用AOP拦截File,FileReader,FileWriter,FileInputStream和FileOutputStream的构造函数?
- 开源,纯Java物理/动态库
- 重新部署后JAX-WS客户端无法连接(SymbolTable中的NullPointerException)
- 在Scala中实现内部特征,就像在Java中使用内部接口一样
- 如何在hibernate中使用条件对列表进行排序