第十五條:使可變性最小
1.什么是不可變類?
(1)需要的所有參數必須在實例化的時候都傳進去。
(2)對象中所有信息在對象的整個生命周期中都保持不變。
2.使類不可變的原則
(1)不要提供任何修改對象狀態的方法。
(2)保證類不會被繼承。
(3)使所有的域都是final類型的。
(4)使所有的域都是私有類型的。
(5)確保對于任何可變組件的互斥性。意思就是,確保在該類的外部不會獲取到該類中可變對象的引用。比如下面這個例子:
public class MyObject{
private final List<String> list = new ArrayList<>;//可變對象
public List<String> getList() {
return new ArrayList(list);
}
public void setList(List<String> list) {
this.list = new ArrayList(list);
}
}
3.不可變類的優點
(1)不可變類簡單。
不可變類只有一種狀態,就是它被創建出來時候的狀態,如果你要根據這個類進行一系列復雜操作,那么這個操作無論在什么時候結果都是相同的,所以你可以直接將結果緩存起來,在下一次執行同樣操作的時候取出來,而不必再進行下一次操作。
(2)不可變類本質上是線程安全的,它不需要同步鎖。
(3)對于不可變類,你永遠都不需要實現拷貝方法。拷貝方法對它來說是沒有意義的。
(4)不可變類可以被自由的共享。
4.不可變對象的缺點
(1)對于每一個不同的值都需要創建一個新的對象。
5.缺點的彌補辦法
(1)先猜測一下經常用到哪些多步驟的操作,然后將它們作為基本數據類型提供。比如Integer,它將值為-128到127的對象緩存起來,但調用valueOf(int i)的時候,直接從緩存中拿,不用再重復創建提高效率。
(2)我們可以創建一個可變配套類。例如String是一個不可變類,它的可變配套類為StirngBuilder和StringBuffer。下面是一個例子,我們現在實現一個復數類,對外提供一個相加的方法如下:
public class Complex {
private final double re;//實部
private final double im;//虛部
private Complex(double re, double im) {
this.re = re;
this.im = im;
}
public static Complex valueOf(double re, double im) {
return new Complex(re, im);
}
public double realPart() {
return re;
}
public double imaginaryPart() {
return im;
}
//復數相加
public Complex add(Complex c) {
return new Complex(re + c.re, im + c.im);
}
}
我們可以為他創建一個配套類:
public class ComplexBuilder {
private double re;
private double im;
private ComplexBuider(double re, double im) {
this.re = re;
this.im = im;
}
public static ComplexBuider newInstance(Complex c) {
return new ComplexBuilder(c.realPart(), c.imaginaryPart());
}
public void add(Complex c) {
this.re = this.re + c.realPart();
this.im = this.im + c.imaginaryPart();
}
public Complex toComplex() {
return Complex.valueOf(this.re, this.im);
}
}
在客戶端中我們如果需要用一個復數和另一個復數相加100次,我們如果不用ComplexBuilder的話就像下面這樣,算上最開始穿件的兩個實例,我們將會創建102個實例:
public class Test {
@Test
public void addNoBuiderTest() throws Exception{
Complex c1 = Complex.valueOf(1, 2);
Complex c2 = Complex.valueOf(2, 3);
for (int i = 0 ; i < 100 ; i++) {
c1 = c1.add(c2);
}
}
}
現在改用ComplexBuilder,現在我們只會創建4個實例:
public class Test {
@Test
public void addNoBuiderTest() throws Exception{
Complex c1 = Complex.valueOf(1, 2);
Complex c2 = Complex.valueOf(2, 3);
ComplexBuilder cb = ComplexBuider.newInstance(c1);
for (int i = 0 ; i < 100 ; i++) {
cb.add(c2);
}
c1 = cb.toComplex();
}
}
6.總結
(1)堅決不要為每個getter都生成setter。
(2)能將類做成不可變的就做成不可變的。
(3)一般比較小的值類都是需要做成不可變的。
(4)對于一些比較大的值類盡量考慮實現成不可變類。
(5)性能方面很有必要的時候才需提供配套類。
(6)如果真的不能作為不可變類,那就盡量限制其可變性
(7)對于一個類的初始化只能在構造器或是靜態工廠中完成。也就是說類的初始化操作(賦值之類的)只能執行一次,就是在構造器或是靜態工廠中。這一點參考java類庫中的TimerTask類。