歡迎光臨
每天分享高質量文章

015:為什麼Java中的字串物件是不可變的

閱讀本文大概需要 4分鐘。

所謂不可變物件,是指一個物件在建立後,它的內部狀態不會被改變的物件。這意味著當我們將一個不可變物件的取用賦值給某個變數後,我們就不能改變該物件的內部狀態。 James Gosling也說過——Java開發者應該儘量使用不可變物件。

在Java中將String物件設定為不可變物件的好處很多,例如:快取、安全、同步、效能等方面。

 

節省記憶體

字串常量池:字串常量池是JVM中的一塊特殊區域(1.7之前存放在perm區,1.8之後存放在堆上),用來存放字串物件的值。在JVM中字串是不可變的,因此JVM對於相同的字元序列,可以只儲存一份,這個特性稱之為“interning”。由於字串是JVM中最常見的物件,因此實現字串共享可以節省很多堆記憶體。

有兩種方式定義的字串,可以存放在常量池中:

  • 使用常量字串初始化字串變數

String s1 = “Hello World”;
String s2 = “Hello World”;
System.out.printlin(s1 == s2); //結果為true


  • 呼叫String物件的intern方法,需要註意的是:直接透過String的構造方法初始化的字串物件,它的值並沒有存放在字串常量池,需要對該物件呼叫intern方法之後,才會將它的值放入字串常量池。

String s1 = “Hello World”;
String s2 = new String(“Hello World”);
System.out.println(s1 == s2); //結果為false

s2.intern();
System.out.println(s1 == s2); //結果為true


 

安全問題

Java應用中使用字串物件存放一些敏感資訊:使用者名稱、密碼、連線地址、IP地址等等。Java中類載入器載入類的時候,也是根據類的名字去檔案系統中的對應路徑去查詢的,類的名稱、對應的路徑,都是使用字串物件儲存的。

將字串物件設計為不可變的,就意味著這個敏感資訊一經生成就不會被改變(有點現在流行的區塊鏈的思路)。

常見的安全檢查流程有兩個步驟:(1)校驗安全資訊;(2)進行敏感操作。如果字串物件是可變的,則在做完第(1)步安全校驗後這個字串物件依然可能被改變。例如,我們現在在維護一個使用者服務,提供了更改使用者暱稱的服務,業務邏輯是先檢查使用者暱稱的合法性,然後再進行資料庫的操作,如果字串物件是可變的,那麼第一步的合法性檢查就沒有意義了

 

併發問題

不可變物件天然具備執行緒安全性,因為不用擔心兩個執行緒同時修改該物件時候產生的爭用問題。假設字串變數str = "hello"被多個執行緒同時使用,如果在某個執行緒中對str賦了新的字串值,那麼就會在字串常量池中生成一份新的字串,不會有併發爭用。

 

hashcode快取

在Java集合框架的很多資料結構中都用到了字串物件,例如HashMapHashTableHashSet等等,在這些資料結構的實現過程中,都使用hashcode()方法來進行hash操作。

由於字串物件的不變性,JDK將它的hashcode()做了快取,這樣對於同一個字串物件,只會在第一次呼叫它的hashcode()方法的時候進行計算,後面的呼叫直接使用快取中的值,這快取也提升了集合資料結構的效能。

 

總結

這個問題考察得比較細緻,需要用對應的案例進行理解,不可變的字串物件對於Java應用來說不可或缺,下圖是寫這篇文章時候用到的思維導圖,也分享給大家。

贊(0)

分享創造快樂