淺析String不可變性


 

在所有編程語言領域,我想字符串應該是地球上最常用的表達手段了吧。

 


 

在java的世界里,String是作為類出現的,核心的一個域就是一個char數組,內部就是通過維護一個不可變的char數組,來向外部輸出的。

這是jdk一段String類定義,首先類是final,表明類不可被繼承;核心域是private final的,final表明這個引用所指向的內存地址不會改變,但這還不足說明value[]是不可變的;因為引用所指向的內存的值有可能發生變化,但是jdk是不會讓這樣的事情發生的。private 保證這個域對外部來說是不可見的,這還不夠,對value還要進行 保護性拷貝 。

舉一個簡單的例子:

這是一個String的構造函數,參數是一個char數組引用,它並沒有把這個數組引用直接賦值給實例對象的value成員變量,而是通過一個Arrays.copyOf的方式拷貝一個數組再給到對象的成員變量。為什么呢?假設它這里是直接一個賦值,那String的不可變性就徹底被破壞了,因為如此一來,存在一個外部引用與實例對象value引用指向相同的內存地址,通過外部引用就可以改變這個char數組對象,最終導致的結果就是String不再不可變。幸好JDK中所有的對value的操作都是保護性拷貝操作,不管是被賦值,還是賦值給其它外部引用。

說了這么多,為什么JAVA要String保持一個不可變的狀態呢?原因其實很簡單,因為String太太太太常用了,地球上沒有能比這個更常用的對象了,設計成不可變的,是為了減少大量的同步鎖的開銷。但是要注意 並不是聲明成final的類一定是不可變的 。

根據effective java一書中提到,類不變需遵循五條規則:

1.不提供任何機會修改對象狀態的方法

2.保證類不被擴展

3.所有域都是final

4.所有域都是私有的

5.確保對於任何可變組件的互斥訪問

 

有興趣的同學可以參考 effective 第十五條,這里就不展開講了。作了那么久的鋪墊,接下來可以談談avoid getfield opcode了,按翻譯來說就是防止"調用訪問域的操作碼",這段tip來自一段注釋。

十分常用的replace方法,內部算法大概是這樣一個過程:先找到第一個oldChar的下標i,拷貝小標i之前的舊數組的內容到新的數組,新數組[i]='newChar',遍歷i之后的內容,若舊數組出現為oldChar則在新數組中替換為newChar,若沒有出現,則拷貝舊值到新數組。

起初我很奇怪,到底為什么,一定要找到第一個出現oldChar的下標,為什么不直接遍歷數組中每一個char 若為舊值,替換為新值。我從時間復雜度,空間復雜度去考慮這個算法,始終沒有得到結果。我還是太年輕啊,后來才發覺其實還是為了維護一個String的設計原則:"對於擁有相同的字符字面量的情況下,String的構造還是優先返回原字符串對象"。這么做應該是為了解決堆內存吧。

 

那么,這一句注釋到底是什么意思呢?要理解這句話,需要對JVM有一定的了解。

 

JVM在運行中的數據區,分為五個部分:方法區,堆區,虛擬機棧,本地方法棧,程序計數器。

首先類相關的信息肯定是放在方法區的,堆中放一些實例對象,程序計數器始終指向下一條將要執行的指令,虛擬機棧和本地方法棧分別是用來於普通方法和本地方法的。

着重說一下虛擬機棧,它是線程私有的,描述的是java方法執行的內存模型:每個方法在執行的同時創建一個棧幀,用於存放局部變量表,操作數棧,方法入口,動態鏈接等。

 

 局部變量表用來存放一些基本數據類,和引用。操作數棧的話,是用來作運算用的,打個比方

int a=1;
int b =2;
int c =a+b;

JVM會先把a,b的值壓入到操作數棧保存起來,等到程序計數器執行加法指令的時候,再把a,b從棧中pop出來。重點就在於a,b值是從哪里壓入到棧中的,如果沒有那么接下來要遍歷的就是value數組了,value毫無疑問是堆中的數據,也就是每一次遍歷會經歷,數據由堆中取出,進入棧幀,再由棧幀壓入到操作數棧,最后pop出來進行運算。

如果執行了上述代碼,情況就大不相同了,val就是一個局部變量,它會被存放在局部變量表中,接來下的運算,就是局部變量表到操作數棧了,屬於一個棧幀內的數據轉移。JVM為了避免頻繁進行堆棧數據轉移,將值復制到本地變量一次,以避免在接下來的幾行中循環的每一次迭代,從堆中多次取下的字段值。

 

除了我提到的這些,String源碼中還有許多值得學習的算法,設計方式,代碼優化,比如,還有常量池的實現。

 

最后我想求教一個問題啊,在JDK7點版本中,String引入了一段靜態代碼塊,

我甚是不解啊,不知道有沒有大神幫我解讀一下這段代碼的含義?


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM