Java理论与实践: 变还是不变?

不变对象是指在实例化后其外部可见状态无法更改的对象。Java 类库中的 String 、 Integer 和 BigDecimal 类就是不变对象的示例 ― 它们表示在对象 的生命期内无法更改的单个值。

不变性的长处

如果正确使用不变类,它们会极大地简化编程。因为它们只能处于一种状态 ,所以只要正确构造了它们,就决不会陷入不一致的状态。您不必复制或克隆不 变对象,就能自由地共享和高速缓存对它们的引用;您可以高速缓存它们的字段 或其方法的结果,而不用担心值会不会变成失效的或与对象的其它状态不一致。 不变类通常产生最好的映射键。而且,它们本来就是线程安全的,所以不必在线 程间同步对它们的访问。

自由高速缓存

因为不变对象的值没有更改的危险,所以可以自由地高速缓存对它们的引用 ,而且可以肯定以后的引用仍将引用同一个值。同样地,因为它们的特性无法更 改,所以您可以高速缓存它们的字段和其方法的结果。

如果对象是可变的,就必须在存储对其的引用时引起注意。请考虑清单 1 中 的代码,其中排列了两个由调度程序执行的任务。目的是:现在启动第一个任务 ,而在某一天启动第二个任务。

清单 1. 可变的 Date 对象的潜在问题

Date d = new Date();
  Scheduler.scheduleTask(task1, d);
  d.setTime(d.getTime() + ONE_DAY);
  scheduler.scheduleTask(task2, d);

因为 Date 是可变的,所以 scheduleTask 方法必须小心地用防范措施将日 期参数复制(可能通过 clone() )到它的内部数据结构中。不然, task1 和 task2 可能都在明天执行,这可不是所期望的。更糟的是,任务调度程序所用的 内部数据结构会变成讹误。在编写象 scheduleTask() 这样的方法时,极其容易 忘记用防范措施复制日期参数。如果忘记这样做,您就制造了一个难以捕捉的错 误,这个错误不会马上显现出来,而且当它暴露时人们要花较长的时间才会捕捉 到。不变的 Date 类不可能发生这类错误。

固有的线程安全

大多数的线程安全问题发生在当多个线程正在试图并发地修改一个对象的状 态(写-写冲突)时,或当一个线程正试图访问一个对象的状态,而另一个线程 正在修改它(读-写冲突)时。要防止这样的冲突,必须同步对共享对象的访问 ,以便在对象处于不一致状态时其它线程不能访问它们。正确地做到这一点会很 难,需要大量文档来确保正确地扩展程序,还可能对性能产生不利后果。只要正 确构造了不变对象(这意味着不让对象引用从构造函数中转义),就使它们免除 了同步访问的要求,因为无法更改它们的状态,从而就不可能存在写-写冲突或 读-写冲突。

不用同步就能自由地在线程间共享对不变对象的引用,可以极大地简化编写 并发程序的过程,并减少程序可能存在的潜在并发错误的数量。

在恶意运行的代码面前是安全的

把对象当作参数的方法不应变更那些对象的状态,除非文档明确说明可以这 样做,或者实际上这些方法具有该对象的所有权。当我们将一个对象传递给普通 方法时,通常不希望对象返回时已被更改。但是,使用可变对象时,完全会是这 样的。如果将 java.awt.Point 传递给诸如 Component.setLocation() 的方法 ,根本不会阻止 setLocation 修改我们传入的 Point 的位置,也不会阻止 setLocation 存储对该点的引用并稍后在另一个方法中更改它。(当然, Component 不这样做,因为它不鲁莽,但是并不是所有类都那么客气。)现在, Point 的状态已在我们不知道的情况下更改了,其结果具有潜在危险 ― 当点实 际上在另一个位置时,我们仍认为它在原来的位置。然而,如果 Point 是不变 的,那么这种恶意的代码就不能以如此令人混乱而危险的方法修改我们的程序状 态了。

时间: 2024-09-07 09:44:41

Java理论与实践: 变还是不变?的相关文章

Java理论与实践专题

Java理论与实践: JDK 5.0中更灵活.更具可伸缩性的锁定机制 Java理论和实践: 一个有缺陷的微基准的剖析 Java理论和实践: 理解JTS ― 平衡安全性和性能 Java理论和实践: 理解JTS ― 幕后魔术 Java理论和实践: 安全构造技术 Java理论与实践: 平衡测试,第3部分:用方面检验设计约束 Java理论与实践:平衡测试,第2部分:编写和优化bug检测器 Java理论与实践:平衡测试,第1部分:不要仅编写测试,还要编写bu Java理论与实践: 您的小数点到哪里去了?

Java 理论与实践:变还是不变?

不变对象具有许多能更方便地使用它们的特性,包括不严格的同步需求和不必考虑数据讹误就能自由地共享和高速缓存对象引用.尽管不变性可能未必对于所有类都有意义,但大多数程序中至少有一些类将受益于不可变.在本月的 Java 理论与实践中,Brian Goetz 说明了不变性的一些长处和构造不变类的一些准则.请在附带的论坛中与作者和其他读者分享您关于本文的心得.(也可以单击文章顶部或底部的"讨论"来访问论坛.) 不变对象是指在实例化后其外部可见状态无法更改的对象.Java 类库中的 String.

Java 理论与实践: JDK 5.0 中更灵活、更具可伸缩性的锁定机制

伸缩 内容: synchronized 快速回顾 对 synchronized 的改进 比较 ReentrantLock 和 synchronized 的可伸缩性 条件变量 这不公平 结束语 参考资料 关于作者 对本文的评价 相关内容: Java 理论与实践 系列 Synchronization is not the enemy Reducing contention IBM developer kits for the Java platform (downloads) 订阅: develop

Java理论与实践: 构建一个更好的HashMap

ConcurrentHashMap 是 Doug Lea的 util.concurrent 包的一部分,它提供 比Hashtable 或者 synchronizedMap 更高程度的并发性.而且,对于大多数成 功的 get() 操作它会设法避免完全锁定,其结果就是使得并发应用程序有着非 常好的吞吐量.这个月,BrianGoetz 仔细分析了 ConcurrentHashMap的代码, 并探讨 Doug Lea 是如何在不损失线程安全的情况下取得这么骄人成绩的. 在7月份的那期 Java理论与实践

Java理论与实践: 修复Java内存模型,第1部分

活跃了将近三年的 JSR 133,近期发布了关于如何修复 Java 内存模型 (Java Memory Model, JMM)的公开建议.原始 JMM 中有几个严重缺陷,这导 致了一些难度高得惊人的概念语义,这些概念原来被认为很简单,如 volatile .final 以及 synchronized.在这一期的 Java 理论与实践 中,Brian Goetz 展示了如何加强 volatile 和 final 的语义,以修复 JMM.这些更改有些已经 集成在 JDK 1.4 中:而另一些将会包含

Java 理论和实践: 了解泛型 识别和避免学习使用泛型过程中的陷阱

简介: JDK 5.0 中增加的泛型类型,是 Java 语言中类型安全的一次重要改进.但是,对于初次使用泛型类型的用户来说,泛型的某些方面看起来可能不容易明白,甚至非常奇怪.在本月的"Java 理论和实践"中,Brian Goetz 分析了束缚第一次使用泛型的用户的常见陷阱.您可以通过 讨论论坛与作者和其他读者分享您对本文的看法.(也可以单击本文顶端或底端的 讨论来访问这个论坛.) 表面上看起来,无论语法还是应用的环境(比如容器类),泛型类型(或者泛型)都类似于 C++ 中的模板.但是

Java 理论与实践: 非阻塞算法简介

[本文转载自Java 理论与实践: 非阻塞算法简介]Java 5.0 第一次让使用 Java 语言开发非阻塞算法成为可能,java.util.concurrent 包充分地利用了这个功能.非阻塞算法属于并发算法,它们可以安全地派生它们的线程,不通过锁定派生,而是通过低级的原子性的硬件原生形式 -- 例如比较和交换.非阻塞算法的设计与实现极为困难,但是它们能够提供更好的吞吐率,对生存问题(例如死锁和优先级反转)也能提供更好的防御.在这期的 Java 理论与实践 中,并发性大师 Brian Goet

Java理论与实践: 您的小数点到哪里去了?

许多程序员在其整个开发生涯中都不曾使用定点或浮点数,可能的例外是, 偶尔在计时测试或基准测试程序中会用到.Java语言和类库支持两类非整数类型 ― IEEE 754 浮点( float 和 double ,包装类(wrapper class)为 Float 和 Double ),以及任意精度的小数( java.math.BigDecimal ).在本月的 Java 理论和实践中,Brian Goetz 探讨了在 Java 程序中使用非整数类型时一 些常碰到的陷阱和"gotcha". 虽

Java理论与实践: 垃圾收集简史

Java 语言可能是使用最广泛的依赖于垃圾收集的编程语言,但是它并不是第 一个.垃圾收集已经成为了包括 Lisp.Smalltalk.Eiffel.Haskell.ML. Scheme和 Modula-3 在内的许多编程语言的一个集成部分,并且从 20 世纪 60 年代早期就开始使用了.在 Java 理论与实践的本篇文章中,Brian Goetz 描述 了垃圾收集最常用的技术. 垃圾收集的好处是无可争辩的 ―― 可靠性提高.使内存管理与类接口设计 分离,并使开发者减少了跟踪内存管理错误的时间.著