不變模式
不變模式就是為了盡可能的去除並行中的同步操作,提高並行程序的性能,可以使用一種不可改變的對象,依靠對象的不變性,可以確保其在沒有同步操作的多線程環境中依然始終保持內部狀態的一致性和正確性。並且,不變模式通過回避問題而不是解決問題的態度來處理多線程並發訪問控制。
不變模式的主要使用場景
- 當對象創建后,其內部狀態和數據不再發生任何變化。
- 對象需要被共享,被多線程頻繁訪問。
不變模式的實現(保證當對象被創建后,不發生任何改變)
- 去除setter方法以及所有修改自身屬性的方法。
- 將所有屬性設置為私有,並用final標記,確保不能被修改
- 確保沒有子類可以重載
- 有一個可以創建完整對象的構造函數
對於第四點,在創建對象時,必須指定完整的數據,因為創建后,就無法進行修改了
eg:
public final class Book { private final String name; private final String ID; public Book(String name,String ID) { this.name=name; this.ID=ID; } public String getName() { return name; } public String getID() { return ID; } }
在不變模式中,final關鍵字起到了重要的作用。
- 對屬性的final定義確保所有數據只能在對象被構造時賦值一次,之后,就永遠不發生改變。
- 對class 的final確保了類不會有子類
注:根據里式原則,子類可以完全的替代父類。如果父類是不變的,那么子類也必須是不變的,但實際上為了防止子類做出一些意外的行為,這里干脆就把子類禁用了
關於String中的不變模式
在JDK中,不變模式有很多應用,如Integer,Long等所有元數據類的包裝類,當然,最典型的就是String類了。
對於String類而言,內部是這樣定義的:public final class String 是一個被final關鍵字修飾的類,我們來看看上面說到final關鍵字的作用,這就意味着:
- String中的數據只能在String對象被構建時賦值,之后就永遠不能改變
- String類不會有子類重載它的方法,也確保了String類生成的對象就是String對象,而不可能是其他類的對象
為什么String要被設計成不變模式呢?
很明顯,當然是為了安全呀
先看到String源碼中的如何賦值的
/** The value is used for character storage. */ private final char value[];
注釋部分說的很明白,它是用一個final的字符數組來儲存字符串的。
雖然value是不可變,但只是value這個引用地址不可變。但是Array數組是可變!!!
因為Array變量只是stack上的一個引用,數組的本體結構在堆。如圖:
String類里的value用final修飾,只是說stack里的這個叫value的引用地址不可變。沒有說堆里array本身數據不可變。
eg:
final char value[]={'a','b','c'}; char value2[]={'d'}; //value=value2; //編譯器報錯,因為用了final修飾 value[0]='d'; for (char v : value) { System.out.print(v);//輸出 dbc }
很明顯,這個value雖然被聲明成final,但是如果value溢出了,就很容被修改了。
幸好,在設計String 的時候,value不僅是final的,而且還是private的,並且在后面所有String的方法里很小心的沒有去動Array里的元素,沒有暴露內部成員字段。
再者,String基本約定中最重要的一條是immutable。String是幾乎每個類都會使用的類,所以保護String的不可變很重要,所以整個String類被設成final,從而實現禁止繼承,避免被其他人繼承后破壞。
假如String沒有聲明為final, 那么String的子類就有可能是被復寫為mutable的,這樣就打破了成為共識的基本約定。並且會造成不可想象的后果,
如,Hashmap之類的集合的key值,mutable的String有非常大的風險,可能會破壞了Hashmap鍵值的唯一性等。
安全之外,還可以涉及到性能方面:
String在堆中維護着一個字符串常量池。如字符串one和two都賦值為"something"。它們其實都指向同一個內存地址。
String one = "someString";
String two = "someString";
這樣在大量使用字符串的情況下,可以節省內存空間,提高效率。
More:
Why String is Immutable or Final in Java?