在面試中經常遇到這樣的問題: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類。