一. 概述
Java中String是一個重要的類,它存在於java.lang包中,出於某種安全性考慮,String類被final修飾,致使它不能被繼承,同時成員方法無法被重寫。所以String類型的變量被稱之爲字符長常量,字符串長度不可變,且只能被賦值一次,不可修改。爲了需要修改String類型的變量從而有了StringBuffer和StringBulider。
二. String
String類型的數據是不可變的,每次改變String類型數據時,就會生成一個新的String對象。而且String類型的數據時被存放在常量池中,如果一個新建的String數據在常量池中存在就直接使用常量池中的數據,如果不存在,就會重寫建立一個內存空間保存這個新建的數據。
三. StringBuffer(JDK1.0)
StringBuffer:字符串變量(Synchronized,即線程安全)。如果要頻繁對字符串內容進行修改,出於效率考慮最好使用 StringBuffer,如果想轉成 String 類型,可以調用 StringBuffer 的 toString() 方法。
Java.lang.StringBuffer 線程安全的可變字符序列。在任意時間點上它都包含某種特定的字符序列,但通過某些方法調用可以改變該序列的長度和內容。可將字符串緩衝區安全地用於多個線程。
StringBuffer 上的主要操作是 append 和 insert 方法,可重載這些方法,以接受任意類型的數據。每個方法都能有效地將給定的數據轉換成字符串,然後將該字符串的字符追加或插入到字符串緩衝區中。
-
append 方法始終將這些字符添加到緩衝區的末端;
-
insert 方法則在指定的點添加字符。
例如,如果 z 引用一個當前內容是 start 的字符串緩衝區對象,則此方法調用 z.append("le") 會使字符串緩衝區包含 startle ,而 z.insert(4, "le") 將更改字符串緩衝區,使之包含 starlet 。
注意:java中的數組,或者是字符產位數的第幾位都是從0開始算。
四. StringBuilder(JDK5.0)
StringBuilder:字符串變量(非線程安全)。在內部,StringBuilder 對象被當作是一個包含字符序列的變長數組。
java.lang.StringBuilder 是一個可變的字符序列,是 JDK5.0 新增的。此類提供一個與 StringBuffer 兼容的 API,但不保證同步。該類被設計用作 StringBuffer 的一個簡易替換,用在字符串緩衝區被單個線程使用的時候(這種情況很普遍)。
其構造方法如下:
構造方法 | 描述 |
StringBuilder() | 創建一個容量爲16的StringBuilder對象(16個空元素) |
StringBuilder(CharSequence cs) | 創建一個包含cs的StringBuilder對象,末尾附加16個空元素 |
StringBuilder(int initCapacity) | 創建一個容量爲initCapacity的StringBuilder對象 |
StringBuilder(String s) | 創建一個包含s的StringBuilder對象,末尾附加16個空元素 |
在大部分情況下,StringBuilder > StringBuffer。這主要是由於前者不需要考慮線程安全。
三者區別
String 類型和 StringBuffer 的主要性能區別:String 是不可變的對象, 因此在每次對 String 類型進行改變的時候,都會生成一個新的 String 對象,然後將指針指向新的 String 對象,所以經常改變內容的字符串最好不要用 String ,因爲每次生成對象都會對系統性能產生影響,特別當內存中無引用對象多了以後, JVM 的 GC 就會開始工作,性能就會降低。
使用 StringBuffer 類時,每次都會對 StringBuffer 對象本身進行操作,而不是生成新的對象並改變對象引用。所以多數情況下推薦使用 StringBuffer ,特別是字符串對象經常改變的情況下。
在某些特別情況下, String 對象的字符串拼接其實是被 Java Compiler 編譯成了 StringBuffer 對象的拼接,所以這些時候 String 對象的速度並不會比 StringBuffer 對象慢,例如:
String s1 = “This is only a” + “ simple” + “ test”;//字符串常量拼接
StringBuffer Sb = new StringBuilder(“This is only a”).append(“ simple”).append(“ test”);
進行字符常量的拼接生成 String s1 對象的速度並不比 StringBuffer 慢。其實在 Java Compiler 裏,自動做了如下轉換,即ava Compiler直接把上述第一條語句編譯爲:
String s1 = “This is only a simple test”;
所以速度很快。但要注意的是,如果拼接的字符串來自另外的 String 對象的話,Java Compiler 就不會自動轉換了,速度也就沒那麼快了,例如:
String s2 = “This is only a”;
String s3 = “ simple”;
String s4 = “ test”;
String s1 = s2 + s3 + s4;//進行字符串對象的拼接
這時候,Java Compiler 會規規矩矩的按照原來的方式去做,String 的 concatenation(即+)操作利用了 StringBuilder(或StringBuffer)的append 方法實現,此時,對於上述情況,若 s2,s3,s4 採用 String 定義,拼接時需要額外創建一個 StringBuffer(或StringBuilder),之後將StringBuffer 轉換爲 String,若採用 StringBuffer(或StringBuilder),則不需額外創建 StringBuffer。
使用策略
-
(1)基本原則:如果要操作少量的數據,用String ;單線程操作大量數據,用StringBuilder ;多線程操作大量數據,用StringBuffer。
-
(2)不要使用String類的"+"來進行頻繁的拼接,因爲那樣的性能極差的,應該使用StringBuffer或StringBuilder類,這在Java的優化上是一條比較重要的原則。例如:
String result = "";
for (String s : hugeArray) {
result = result + s;
}
// 使用StringBuilder
StringBuilder sb = new StringBuilder();
for (String s : hugeArray) {
sb.append(s);
}
String result = sb.toString();
-
當出現上面的情況時,顯然我們要採用第二種方法,因爲第一種方法,每次循環都會創建一個String result用於保存結果,除此之外二者基本相同(對於jdk1.5及之後版本)。
-
(3)爲了獲得更好的性能,在構造 StringBuffer 或 StringBuilder 時應儘可能指定它們的容量。當然,如果你操作的字符串長度(length)不超過 16 個字符就不用了,當不指定容量(capacity)時默認構造一個容量爲16的對象。不指定容量會顯著降低性能。
-
(4)StringBuilder 一般使用在方法內部來完成類似 + 功能,因爲是線程不安全的,所以用完以後可以丟棄。StringBuffer 主要用在全局變量中。
-
(5)相同情況下使用 StringBuilder 相比使用 StringBuffer 僅能獲得 10%~15% 左右的性能提升,但卻要冒多線程不安全的風險。而在現實的模塊化編程中,負責某一模塊的程序員不一定能清晰地判斷該模塊是否會放入多線程的環境中運行,因此:除非確定系統的瓶頸是在 StringBuffer 上,並且確定你的模塊不會運行在多線程模式下,纔可以採用 StringBuilder;否則還是用 StringBuffer。