在面向对象的编程领域中,一个对象如果在创建后,它的状态不能改变,那么我们就认为这个对象是不可变的(Immutable)。 在Java中,String这个不可变对象就是个很好的例子。一旦创建String对象后,我们不能对它的状态进行改变。我们可以创建新的String对象,但是不能改变原有的String对象。 然而,在JDK中有不可变对象只是很少的一部分。类似Date这样的类,我们能够通过调用setTime()方法改变它的状态。 我不清楚为什么JDK的设计者把如此相似的两个对象采取截然相反的实现方式。然而,我认为Date作为一个可变对象有很多缺陷。与此同时,不可变的String更能体现面向对象编程的本质。 更进一步,我认为在一个纯面向对象的世界里,所有的类都应该是不可变的。然而,有时会因为JVM的**很难实现这一点。但不管怎么说,我们都应该尽全力做到最好。 下面几点是支持对象不可变性的一些理由:
线程安全不可变对象最重要的特征是线程安全。这意味着多个线程能够在同时访问同一个对象,而且不需要担心与其他线程产生冲突。 如果对象的方法都不能改变对象的状态,那么不管有多少个对象,不管它们被并行调用的频率不可变对象运行在自己的堆栈中。 Goetz等人在他们一本非常有名的书Java Concurrency in Pratice中更加细致的讨论了不可变对象的优势,强烈推荐大家去看。 避免时间上的耦合下面给出一个时间上耦合的例子(下面的代码发送两个连续的 HTTP POST请求,第二个有HTTP body): 这段代码可以工作。但是,第一个方法必须在第二个方法之前调用,如果我们把第一个方法注释掉(也就是去掉第二行与第三行),编译器不会报任何错误: 现在,这段代码虽然没有编译错误,但仍然失效了。这就是所谓的时间上的耦合总是有些隐藏信息需要程序员去记住。在这个例子中,我们必须记着在使用第二个方法前,需要调用第一个方法。 我们必须记住第二个方法必须与第一个方法一起使用,并且是在第一个方法之后使用。 如果Request对象不可变,第一个代码片段也是不对的,很有可能是下面这个样子: 这下这两个方法就没有耦合了,我们可以很放心的去掉第一个方法。你也许会说上面的代码有重复,确实是有。但是我们可以改成这样: 这样一来,我们重构后的代码也是正确的,而且没有了时间上的耦合。第一个请求可以在不影响第一个请求的情况下取消掉。 我希望这个例子能够向你展示操作不可变对象是更可读且可维护的,因为它没有时间上的耦合。 避免副作用让我们在一个新方法中使用Request对象现在它是可变的了): 下面让我们发送两个请求第一个用GET方法,第二个用POST方法: 这样代码就安全了,而且没有副作用。 避免身份可变性(Identity Mutability)通常而言,对于内部状态相同的对象,我们认为它们是相同的。Date 类就是这方面一个很好的例子: 这里有两个对象,但是由于它们的内部状态是一样的,所以我们认为它们是相同的。可以通过重写它们的equals()与hashCode()方法实现。 这种便捷的方式的后果是:当我们在处理可变对象时,一旦我们改变了它们的内部状态,那么也就改变了它们的身份。 这也许看起来很自然,但是如果我们把可变对象作为Map的key时,情况就不一样了: 当我们改变date的状态时,我们不希望改变它的身份。我们不想仅仅因为改变了key的状态就失去了这个条目。但是上面的例子确实会发生丢失条目的问题。 当我们向map中添加一个对象时,这个对象的hashCode()会返回一个值。HashMap根据这个值来决定当前条目在内部哈希表的位置。当我们调用containsKey()方法时,由于对象的hashcode不一样了(因为 hashcode 依赖于内部状态),所以HashMap在内部的哈希表中找不到相应条目了。 这是个非常烦人的问题,而且很难去调试可变对象的副作用而产生的问题。不可变对象就能从根本上避免这个问题了。 原子性失败下面是个简单的例子: 很明显,如果程序因为溢出而导致抛出异常时,Stack 对象就会处于一种不健康的状态。它的size属性会增加,但是items中并不包含新元素。 不可变性可以避免这个问题,因为一个不可变对象只能在构造时改变状态。构造函数要么失败,这样就不会初始化这个对象;要么成功,这时才会构造一个合法可靠且的对象。因为这时对象的内部属性不会再发生改变了。 如果想了解更多关于这方面的内容,可以参考Joshua Bloch写的Effective Java, 2nd Edition 反对不可变性的论据下面是一些反对不可变性的争论:
如果你有其他的想法,请在下面贴出来,我将尽量回复。 原文出处:javacodegeeks 译文出处: ImportNew - 刘 家财 ////////////////////////// 1. ImportNew 专注 Java 技术分享,欢迎关注。微信号【 importnew 】 2. 点击“阅读原文”,查看本文的网页版。 本文转载自:微信公众账号 - ImportNew,版权归原作者所有! |
|
声明:文章版权归原作者所有 部分文章转自互联网 如有侵权请联系
[邮箱地址] 删除
|