?對String對象的任何改變都不影響到原對象,相關的任何change操作都會生成新的對象。
Java為了避免在一個系統中產生大量的String對象,引入了字符串常量池。其運行機制是:創建一個字符串對象,如果有則不需要創建直接從池中查找到的對象引用;如果沒有則新建字符串對象,返回對象引用,并且將新創建的對象放入池中。但是,通過new方法創建的String對象是不檢查字符串池的,而是直接在堆區或棧區創建一個新的對象,也不會把對象放入池中。舉例:String str1 = “123”;//通過直接賦值方式,放入字符串常量池
String str2 = new String(“123”);//通過new方式賦值方式,不放入字符串常量池
String的特性:
[A] 不可變,是指String對象一旦生成,則不能再對它進行改變。不可變的作用主要在于當一個對象需要被多線程共享,并且訪問頻繁時,可以省略同步和鎖等待的時間,從而大幅度提高系統性能。不可變模式是一個可以提高多線程程序的性能,降低多線程程序復雜度的設計模式。
[B] 針對常量池的優化。當2個String對象擁有相同的值時,他們只引用常量池中的同一個拷貝。當同一個字符串反復出現時,這個技術可以大幅度節省內存空間。
不可變的好處
1. 可以緩存 hash 值
因為 String 的 hash 值經常被使用,例如 String 用做 HashMap 的 key。不可變的特性可以使得 hash 值也不可變,因此只需要進行一次計算。
2. String Pool 的需要
如果一個 String 對象已經被創建過了,那么就會從 String Pool 中取得引用。只有 String 是不可變的,才可能使用 String Pool。
3. 安全性
String 經常作為參數,String 不可變性可以保證參數不可變。例如在作為網絡連接參數的情況下如果 String 是可變的,那么在網絡連接過程中,String 被改變,改變 String 對象的那一方以為現在連接的是其它主機,而實際情況卻不一定是。
4. 線程安全
String 不可變性天生具備線程安全,可以在多個線程中安全地使用。
? ?String類型和StringBuilder類型的主要性能區別其實在于String是不可變的對象,因此在每次對String類型進行改變的時候其實都等同于生成了一個新的String對象,然后將指針指向新的String對象,所以經常改變內容的字符串最好不要用String,因為每次生成對象都會對系統性能產生影響,特別當內存中無引用對象多了以后,JVM的GC就會開始工作,那速度是一定會相當慢的。而如果是使用StringBuilder類則結果就不一樣了,每次結果都會對StringBuilder對象本身進行操作,而不是生成新的對象,再改變對象引用。所以在一般情況下我們推薦使用StringBuilder,特別是字符串對象經常改變的情況下。
? ?StringBuilder和StringBuffer類擁有的成員屬性以及成員方法基本相同,擁有幾乎一致對外提供的調用接口,其底層在內存中的存儲方式與String相同,都是以一個有序的字符序列(char類型的數組)進行存儲,不同點是StringBuilder/StringBuffer對象的值是可以改變的,并且改變以后對象的引用不會發生改變。兩者對象在構造過程中,首先按照默認大小申請一個字符數組,由于會不斷加入新數據,當超過默認大小后,會創建一個更大的數組,再丟棄舊的數據。因此,對于較大的對象的擴容會涉及大量的內存復制操作,如果能夠預先評估大小,可以提升性能。區別是StringBuffer類的成員方法前面多了一個關鍵字:synchronized,不用多說,這個關鍵字是在多線程訪問時起到安全保護作用的,也就是說StringBuffer是線程安全的。
String, StringBuffer and StringBuilder:
1. 可變性
String 不可變
StringBuffer 和 StringBuilder 可變
2. 線程安全
String 不可變,因此是線程安全的
StringBuilder 不是線程安全的
StringBuffer 是線程安全的,內部使用 synchronized 進行同步
應用場景:
[A] 字符串內容不經常發生變化的業務場景優先使用String類。例如:常量聲明、少量的字符串拼接操作等。
[B] 在頻繁進行字符串的運算(如拼接、替換、刪除等),并且運行在多線程環境下,建議使用StringBuffer,例如XML解析、HTTP參數解析與封裝。
[C] 在頻繁進行字符串的運算(如拼接、替換、刪除等),并且運行在單線程環境下,建議使用StringBuilder,例如SQL語句拼裝、JSON封裝等。
String自身的演化:
在沒有線程安全問題的情況下,全部拼接操作是應該都用StringBuilder嗎?畢竟這樣書寫的代碼,還是要多敲很多字的,可讀性也不理想,下面的對比非常明顯。
String strByBuilder = newSrtingBuilder().append(‘aa’).append(‘bb’).append(‘cc’).append(‘dd’).toString();
String strByConcat = ‘aa’ + ‘bb’ + ‘cc’ + ‘dd’;
通過反編譯可以看到,在JDK8中,字符串拼接操作會自動被javac轉換為StringBuilder操作,而在JDK9里面則是因為Java9為了更加統一字符串操作優化,提供了StringConcatFactory,作為一個統一的入口。所以在JDK8之后可以直接使用String類型。
Java的字符串在歷史版本中,是使用char數組來存數據的,這樣非常直接。但是Java中的char是兩個bytes大小,拉丁語系語言的字符,根本就不需要太寬的char,這樣無區別的實現就造成了一定的浪費。在Java9中,引入了Compact Strings的設計,對字符串進行了大刀闊斧的改進。將數據存儲方式從char數組,改變為一個byte數組加上一個標識編碼的所謂coder,并且將相關字符串操作類都進行了修改。原來char數組的實現,字符串的最大長度就是數組本身的長度限制,但是替換成byte數組,同樣數組長度下,存儲能力是原來的一半。