java immutability 不变性

最近可能要写多线程程序,学写备忘,

<effective java > item 13
<java concurrency in practice> 3.4. Immutability


谈优先选择不变类

不变类就是实例不能被改变的类, Effective Java 的 Item13 详细的探讨了为什么设计中要优先考虑将一个类设计为不变的,在细读后感觉平时自己的设计和这个观念有比较大的沟壑,值得总结一下这个 item ,并反思一下如何去实践这个 best practice 。

不变类的优点有如下几个:容易设计,实现和使用,不易出错,安全性更好。

不变类很简单,它只会有一个状态,所以一旦在实例化的时候保证了这个不变实例复合某些业务逻辑上的要求,它就会一直,保持这些要求和规约得到满足。而一个可变类,则相对来说不可靠得多。

不变类是线程安全的,不需要考虑同步的问题,可以安全地共享。同时也不需要在共享过程中的传递引用环节使用“防御性复制”( item24 详谈,主要是保持数据的复制而不是引用的复制)。

由于不变类的上述优点,使得它非常适合成为其他类的组成部分,如果你明确了一个组件是不变类,则能在维护整体的业务逻辑稳定性方面轻松很多。非常显著的一个例子就是用于 map 的 keys 和 set 的 elements 时,不变类的稳定性保证了数据不会不小心被改变。反之,如果是用可变类, key 对象的一次逻辑上不恰当的变动(语法上无错,可以通过编译)可能导致另一处代码在 map 中获取 elements 时找到不正确的值,而实际经验中,我们深刻体会到的一件事情就是不被编译器发现而且不抛出异常的程序错误往往会浪费极大的精力,而且要靠好的视力和丰富的调试经验才能发现。

不变类的唯一缺点就是对应每个不同的值,必须存在一个单独的实例。当需要频繁变更值的时候,性能大为降低,典型的例子就是拿着 String 来反复折腾,而不用 StringBuffer 。而 StringBuffer 正是用来解决这一缺点的,当一个不变类可能需要频繁改变值的时候,就需要设计这么一个对应的可变类来暂时替身。

那么怎么在实际设计中贯彻这种优先选择不变类的思想呢,总的原则就是:除非你找到非常好的理由来设计为一个可变类,否则就应该设计为不变类,要有意的抵抗为每个变量配上 getter 和 setter 的冲动,当然我不是在说你要违反 javabean 的基本要求。。。基本上,小的存放数值的对象都应该设计为不变类,如果该类的值经常改,就配上对应的可变类。而总有些类显然不可能是不变类的,这时也要尽可能的限制它的可变性。类的构造器应该实例化一个充分满足逻辑不变式的实例,而不要交出一个半成品,也不应该提供构造器之外的一个所谓的初始化方法,除非是有极好的理由。同理,也不要提供“重设初始化”方法以达到实例复用的目的,比起它带来的危害,这个垃圾回收环保复用的思路带来的性能提升实在微不足道。



最后,归纳一下设计不变类的 5 条规则:

1. 不提供任何修改对象的方法。

2. 保证没有任何一个方法会被覆盖。

3. 所有的域都是 final 的。

4. 所有的域都是 private 的。

5. 如果不变类内部包括可变的子对象,保证它绝对不会被其他代码获取引用。



要解释的是第 5 ,第 4 点并不能保证这一点,要做到第 5 点,就必须注意在不变类的实例化中,决不通过其他代码提供的引用来赋值给这个子对象,也决不提供这个子对象的引用的获取方法,因为这些传入传出的引用在别的代码中可以被用来修改对象的域。在构造器,读取方法中使用防御性复制,只传值,不传引用。而且是递归性的防御性复制,关于这一话题,在以后的随笔中再详细探讨。

好了,这一篇就到这里,我从中学到的东西,总结起来一句话,就是要非常吝啬类的可变性,从而获取坚固,不易错的类结构。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值