1. 程式人生 > >Java 不可變類

Java 不可變類

本文整理自《Effective Java》一書。

不可變類,並在物件的整個生命週期(lifetime)內保持不變。Java平臺類庫中包含許多不可變的類,其中有String、基本型別的包裝類、BigInteger、BigDecimal

為了使類成為不可變,要遵循下面五條規則:

1.      不要提供任務會修改物件狀態的方法。

2.      保證類不會被擴充套件。常見做法final Class、 private constructor並新增公有的靜態工廠(static factory)來代替公有的構造器,如 public                static Complex valueOf(){ return new Complex();}。

3.      使所有的域都是final的。final

4.      使所有的域都成為私有的。private

5.      確保對於任何可變元件的互斥訪問。採用函式的(functional)做法,防止this引用逸出,如String中的做法:

public static String copyValueOf(char data[]) {
        return new String(data);
}

不可變物件本質上是執行緒安全的,它們不要求同步。當多個執行緒併發訪問這樣的物件時,它們不會遭到破壞,所以,不可變物件可以被自由的共享。不可變類應該充分利用這種優勢,鼓勵客戶端儘可能地重用現有的例項,對於頻繁用到的值,為它們提供公有的static final 常量

,如 Integer 提供的 public static final intMIN_VALUE =-231  

不可變類的真正唯一的缺點是,對於每個不同的值都需要一個單獨的物件。建立這種物件的代價可能很高,特別是對於大型物件的情形。所以,最好的辦法是提供一個公有的可變配套類。在Java平臺類庫中,這種方法的主要例子是String類,以及它的可變配套類StringBuilder(和基本上已經廢棄的StringBuffer)

當BigInteger和BigDecimal剛被編寫出來的時候,對於“不可變的類必須為final的”還沒有得到廣泛地理解,所以它們的所有方法都可能被覆蓋,遺憾的是,為了保持向後相容,這個問題一直無法得以修正。

public class BigDecimal
extends Number
implements Comparable<BigDecimal>

如果你在編寫一個類,它的安全性依賴於BigInteger或者BigDecimal引數的不可變性,就必須進行檢查,以確定這個引數是否為“真正的”BigInteger或者BigDecimal,而不是不可信任子類的例項。如果是後者的話,就必須在假設它可能是可變的前提下對它進行保護性拷貝:

public static BigInteger safeInstance(BigInteger val){
	if(val.getClass()!= BigInteger.class){
		return new BigInteger(val.toByteArray());
	}
	return val;
}

實際中,上述規則規定不可變類的所有域都必須是final的。這個要求為了提高效能可以有所放鬆,只要沒有一個方法能夠對物件的狀態產生外部可見的改變。如String類的

/** Cache the hash code for the string */private int hash; 採用延遲初始化來快取一些開銷昂貴的計算結果到這些域中

除非有令人信服的理由要使域變成是非final的,否則要使每個域都是final的。關於final域:"When final is used withobject references rather than primitives, the meaning can be confusing. With aprimitive, final makes the value a constant, but with an object reference,final makes the reference a constant. Once the reference is initialized to anobject, it can nerver be changed to point to another object. However, theobject itself can be modified."  FromThinking in Javafinal修飾域時,只保證引用的不可變,而不保證引用指向的物件不可變,除非這個物件本身是不可變。請一定確保公有類的公有static final域所引用的物件都是不可變的