Java基礎——String,StringBuilder,StringBuffer的區別

原文鏈接:https://baijiahao.baidu.com/s?id=1629804867201303563&wfr=spider&for=pc

String是Java中基礎且重要的類,並且String也是Immutable類的典型實現,被聲明爲final class,除了hash這個屬性其它屬性都聲明爲final,因爲它的不可變性,所以例如拼接字符串時候會產生很多無用的中間對象,如果頻繁的進行這樣的操作對性能有所影響。

StringBuffer就是爲了解決大量拼接字符串時產生很多中間對象問題而提供的一個類,提供append和add方法,可以將字符串添加到已有序列的末尾或指定位置,它的本質是一個線程安全的可修改的字符序列,把所有修改數據的方法都加上了synchronized。但是保證了線程安全是需要性能的代價的。

在很多情況下我們的字符串拼接操作不需要線程安全,這時候StringBuilder登場了,StringBuilder是JDK1.5發佈的,它和StringBuffer本質上沒什麼區別,就是去掉了保證線程安全的那部分,減少了開銷。

StringBuffer 和 StringBuilder 二者都繼承了 AbstractStringBuilder ,底層都是利用可修改的char數組(JDK 9 以後是 byte數組)。

所以如果我們有大量的字符串拼接,如果能預知大小的話最好在new StringBuffer 或者StringBuilder 的時候設置好capacity,避免多次擴容的開銷。擴容要拋棄原有數組,還要進行數組拷貝創建新的數組。

我們平日開發通常情況下少量的字符串拼接其實沒太必要擔心,例如

String str = "aa"+"bb"+"cc";

像這種沒有變量的字符串,編譯階段就直接合成"aabbcc"了,然後看字符串常量池(下面會說到常量池)裏有沒有,有也直接引用,沒有就在常量池中生成,返回引用。

如果是帶變量的,其實影響也不大,JVM會幫我們優化了。(以下運行JDK版本爲1.8)

看看反編譯結果,String拼接用的是StringBuilder.append。

是不是好像覺得那不平時不需要用StringBuilder 啊,用String就好了啊,都幫我們優化了?

不是的來看這個情況

看看反編譯結果,new了很多次StringBuilder

也就是說會如果有大量的字符串拼接new好多StringBuilder對象,所以頻繁的字符串操作還是得用StringBuilder!

再說說字符串常量池

看看我們的代碼,你會發現String是真的頻繁得使用到,Java爲了避免在一個系統中產生大量的String對象,引入了字符串常量池。

創建一個字符串時,首先會檢查池中是否有值相同的字符串對象,如果有就直接返回引用,不會創建字符串對象;如果沒有則新建字符串對象,返回對象引用,並且將新創建的對象放入池中。但是,通過new方法創建的String對象是不檢查字符串常量池的,而是直接在堆中創建新對象,也不會把對象放入池中。上述原則只適用於直接給String對象引用賦值的情況。

String str1 = new String("a"); //不檢查字符串常量池的

String str2 = "bb"; //檢查字符串常量池的

String還提供了intern()方法。調用該方法時,如果字符串常量池中包括了一個等於此String對象的字符串(由equals方法確定),則返回池中的字符串的引用。否則,將此String對象添加到池中,並且返回此池中對象的引用。

在JDK6中,不推薦大量使用intern方法,因爲這個版本字符串緩存在永久代中,這個空間是有限了,除了FullGC之外不會被清楚,所以大量的緩存在這容易OutOfMemoryError。

之後的版本把字符串放入了堆中,避免了永久代被擠滿。

總結

1、在字符串不經常發生變化的業務場景優先使用String(代碼更清晰簡潔)。如常量的聲明,少量的字符串操作(拼接,刪除等)。

2、在單線程情況下,如有大量的字符串操作情況,應該使用StringBuilder來操作字符串。不能使用String"+"來拼接而是使用,避免產生大量無用的中間對象,耗費空間且執行效率低下(新建對象、回收對象花費大量時間)。如JSON的封裝等。

3、在多線程情況下,如有大量的字符串操作情況,應該使用StringBuffer。如HTTP參數解析和封裝等。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章