Effective Java - 使可变性最小化
第16条 在公有类中使用访问方法而非公有域
- 对公有类, 应该用包含私有域和公有访问方法(getter)的类来代替, 对可变的类, 加上公有设值方法(setter).
-> 保留了改变内部表现的灵活性. - 如果类是包级私有的, 或者是私有的嵌套类, 直接暴露它的数据域并没有本质的错误.
第17条 使可变性最小化
这一条讲的是不可变类:每个实例中包含的所有信息都必须在创建该实例的时候就提供, 并在对象的整个生命周期内固定不变。Java中比较经典的不可变类就是String
、BigInteger
、BigDecimal
一个不可变类的例子:
public final class Complex {
private final double re;
private final double im;
public Complex(double re, double im) {
this.re = re;
this.im = im;
}
public double realPart() { return re; }
public double imaginaryPart() { return im; }
}
主要就是类里面的每一个字段都用final修饰,并且在构造方法中初始化。
不可变类的5个原则
- 不要提供任何会修改对象状态的方法: 没有set方法
- 保证类不会被扩展: 一般将类声明为final
- 使所有的域都是final的
- 使所有的域都成为私有的.
- 确保对于任何可变组件的互斥访问: 如果有字段是一个对象引用,则确保整个客户端不能拿到这个对象的引用
不可变类的优势
- 不可变对象不叫简单:只有一种状态,不会被修改
- 不可变对象本质上是线程安全的, 它们不要求同步
- 不可变对象可以被自由地共享->不可变对象永远也不需要保护性拷贝
不可变类的缺点
对于每个不同的值都需要一个单独的对象. (特定情况下的性能问题.)
在构建了不可变类的同时,也可以创建配套的可变配套类,比如Java类库中的String
的可变配套类是StringBuilder
和StringBuffer
.
其它不可变类设计方案
-
除了将类设置为
final
外,也可以通过私有化构造方法,用静态工厂代替的方法使类不能被继承。这样可以提供缓存的能力 -
不一定所有的字段都是
final
的,需要满足的要求是: 没有一个方法能够对对象的状态产生外部可见的改变。比如一个类可以有一个非
final
的hashCode字段,用来缓存hashCode
感想
Java14的record就是不可变类的典范。我们正常使用的VO按道理说都应该是不可变的。可是真正在使用过程中,又经常迫于无奈只能在不同的地方改变对象的状态。比如说UI可能只能识别到companyCode,而Dao又需要的是companyId,就只能在service层给criteria塞上通过code查询出来的id。
这个地方应该提醒我们的更多的是带有业务逻辑的Helper、Wrapper等等,这些类里面的成员变量都应该是final的,尤其不能提供set方法。不然在对象的传输、使用过程中,我们很难去追踪哪里可能会有副作用,导致了对象里面的内容变化。其实应该从根本上杜绝这种事情发生。包括我自己曾经有过不好的习惯,在public方法中,把形参塞到当前对象的全局变量上,为了以后其他的private方法可以不需要都传这个形参,直接读取全局变量就可以了。这其实也是一种不好的改变了当前对象状态的一种形式,而且它更加的隐晦