1. 程式人生 > >java中String類為什麼不可變?

java中String類為什麼不可變?

在面試中經常遇到這樣的問題:1、什麼是不可變物件。不可變物件有什麼好處。在什麼情景下使用它,或者更具體一點,java的String類為什麼要設定成不可變型別?

1、不可變物件,顧名思義就是建立後的物件不可以改變,典型的例子有java中的String型別。

2、相比於可變物件,不可變物件有很多優勢:

  (1)不可變物件可以提高String Pool(字串常量池)的效率和安全性。如果你知道一個物件是不可變動 ,那麼需要拷貝的物件的內容時就不用複製它本身二隻是複製它的地址,複製地址(通常一個指標的大小)需要很小的記憶體,效率也很好。二對於其他引用同一個物件的其他變數也不會造成影響。

  (2)可不變物件對於多執行緒滴安全的,因為在多執行緒同事進行的情況下,一個可變物件的值很可能被其他執行緒改變這樣會造成不可預期的結果麼人使用不可變物件就可以避免這種情況出現。

java將String設成不可變最大的原因是效率和安全。

那麼不可變型別到底是怎麼實現的呢?

在java中考慮到各種因素,需要綜合到記憶體,資料結構以及安全的方面的考慮,在下文中,我會為各種原因做一個總結。

1、字串常量池的需要

字串常量池是java堆記憶體中一個特殊的儲存區域,當建立一個String物件,假如此字串值已經存在於常量池中,則不會建立一個新的物件,而是引用已經存在的物件。

程式碼如下:

  String s1 = "ABC";

  String s2 = "ABC";

在java中記憶體分為堆記憶體和棧記憶體,堆記憶體存放的是物件,棧記憶體儲存物件的引用,字串“ABC”存放在堆記憶體中,而s1,s2作為物件的引用則存放在棧記憶體中,原理如下:

      堆記憶體       棧記憶體

 String物件  "ABC"______ s1  String變數的引用

                     |______ s2

假設:字串物件允許改變,那麼將會導致各種邏輯錯誤。比如改變一個物件卻影響到另外一個獨立的物件。

思考一下:一下程式碼,s1和s2還會指向同一個物件嗎?

  String s1 = "AB"+"C";

  String s2 = "A"+"BC";

也許很多新手都會覺得不是指向同一個物件,但是考慮到現代編譯器會進行常規的優化所以他們都會指向常量池中的同一個物件。

2、執行String物件快取HashCode

java中String物件的雜湊碼被頻繁的使用,比如在HashMap的容器中。

字串不變性保證了hash碼的唯一性,因此可以放心的進行快取,這也是一種效能優化手段,意味著不必每次都取計算新的雜湊碼,在String類的定義中有如下程式碼:

  private int hash;//用來快取HashCode

3、安全性

String被許多的Java類(庫)用來當做引數,例如 網路連線地址URL,檔案路徑path,

還有反射機制所需要的String引數等, 假若String不是固定不變的,將會引起各種安全隱患。

總體來說, String不可變的原因包括 設計考慮,效率優化問題,以及安全性這三大方面.

事實上,這也是Java面試中的許多 "為什麼" 的答案。

4、String類不可變的好處

String是所有語言中最常用的一個類。我們知道在Java中,String是不可變的、final的。Java在執行時也儲存了一個字串池(String pool),這使得String成為了一個特別的類。

String類不可變性的好處

1.只有當字串是不可變的,字串池才有可能實現。字串池的實現可以在執行時節約很多heap空間,因為不同的字串變數都指向池中的同一個字 符串。但如果字串是可變的,

那麼String interning將不能實現(譯者注:String interning是指對不同的字串僅僅只儲存一個,即不會儲存多個相同的字串。),因為這樣的話,如果變數改變了它的值,那麼

其它指向這個值的變數 的值也會一起改變。

2.如果字串是可變的,那麼會引起很嚴重的安全問題。譬如,資料庫的使用者名稱、密碼都是以字串的形式傳入來獲得資料庫的連 接,或者在socket程式設計中,主機名和埠都是以字

符串的形式傳入。因為字串是不可變的,所以它的值是不可改變的,否則黑客們可以鑽到空子,改變字元 串指向的物件的值,造成安全漏洞。

3.因為字串是不可變的,所以是多執行緒安全的,同一個字串例項可以被多個執行緒共享。這樣便不用因為執行緒安全問題而使用同步。字串自己便是執行緒安全的。

4.類載入器要用到字串,不可變性提供了安全性,以便正確的類被載入。譬如你想載入java.sql.Connection類,而這個值被改成了myhacked.Connection,那麼會對你的資料庫造成

不可知的破壞。

5.因為字串是不可變的,所以在它建立的時候hashcode就被快取了,不需要重新計算。這就使得字串很適合作為Map中的鍵,字串的處理速度要快過其它的鍵物件。這就是HashMap

中的鍵往往都使用字串。  

二、既然知道String型別不可變的好處和作用那麼大,那麼是否就不需要可變型別了呢?

當然不是,當你需要向字串插入或修改的時候,Sting不可變型別就顯得足襟見肘,這時候就需要一個可變的字串型別:StringBuffer。

StringBuffer與String一樣,都代表字串,但是由於StringBuffer內部實現的方式和String不同,所以StringBuffer在處理字串的時候

不產生新的物件,在記憶體使用上要由於String類。