String、StringBuilder和StringBuffer的區別
String | StringBuilder | StringBuffer | |
---|---|---|---|
是否可變 | 不可變 | 可變 | 可變 |
線程安全 | 安全1 | 不安全 | 安全 |
拼接方法 | + 或 concat 方法 |
append 方法 |
append 方法 |
拼接性能 | 最差2 | 最好 | 中間3 |
適用情況 | 很少字符串操作 | 單線程大量字符串操作 | 多線程大量字符串操作 |
- 不可變對象天然是線程安全的,因爲其只可讀,不可寫。
- 在拼接兩個
String
對象時,會創建一個新的String
對象保存拼接的結果,因此性能較差,並且會產生較多的內存垃圾。而如果拼接的都是字符串字面量,會在編譯時期優化。 StringBuffer
由於要保證線程安全,因此比StringBuilder
的性能差。
String類的不可變性
如何保證不可變性
Java中String
類的源碼前兩行如下(版本是jdk_1.8.0_221):
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
// ...
}
在Java9以前字符串採用
char[]
數組來保存字符,因此字符串的每個字符佔2字節;從Java9開始,字符串採用byte[]
數組再加一個encoding-flag字段來保存字符,因此Java9的字符串更加節省空間。
Java通過以下措施聯合保證了String
類的不可變性:
- 用
final
修飾value成員字段,保證其不被修改爲其他數組對象。 String
類的所有方法都不會修改value字段中的任何元素。- 用
final
修飾String
類,將其設計爲不可繼承的,保證了value字段不會被子類獲取到。
不可變性的優點
- 字符串不變保證了哈希值不變,因此
String
類中緩存了哈希值,當字符串對象作爲HashMap
的鍵時,能夠提高效率。 - 字符串對象作爲Java中非常常見的對象,針對字符串對象的空間優化能夠大幅降低JVM的內存開銷。其不變性讓**字符串常量池(String Pool)**成爲可能:
- 採用字面值的方式創建一個字符串時(如
String str = "aaa";
),JVM首先會去字符串池中查找是否存在"aaa"這個對象,如果不存在,則在字符串池中創建"aaa"這個對象,然後將池中"aaa"這個對象的引用地址返回給字符串常量str,這樣str會指向池中"aaa"這個字符串對象;如果存在,則不創建任何對象,直接將池中"aaa"這個對象的地址返回,賦給字符串常量。 - 採用new關鍵字新建一個字符串對象時(如
String str2 = new String("aaa")
),JVM首先在字符串池中查找有沒有"aaa"這個字符串對象,如果有,則不在池中再去創建"aaa"這個對象了,直接在堆中創建一個"aaa"字符串對象,然後將堆中的這個"aaa"對象的地址返回賦給引用str2;如果沒有,則首先在字符串池中創建一個"aaa"字符串對象,然後再在堆中創建一個"aaa"字符串對象,然後將堆中這個"aaa"字符串對象的地址返回賦給str2引用。 - 字符串的常量池能夠減少相同字符串的重複創建,優化空間。
- 採用字面值的方式創建一個字符串時(如
- 不可變對象是線程安全的,同一個字符串實例可以被多個線程共享。
- 類加載器要用到字符串,不可變性提供了安全性,以便正確的類被加載。
總的來說,字符串的不可變設計是考慮到效率和安全性的。