Java 中有三個類別用於處理文字資料:String、StringBuffer和StringBuilder。每個開發人員在學習語言之初都會遇到第一個問題。剩下的兩個呢?它們有什麼區別,什麼時候使用一個或另一個類別更好?一般來說,它們之間的差異很小,但最好在實踐中理解一切:)
您可以在 JavaRush 課程的 Java 多執行緒任務的第二級中更詳細地研究該主題:
字串類
這個類別代表一個字元序列。程式中定義的所有字串文字(例如“This is String”)都是 String 類別的實例。 字串有兩個基本特徵:- 這是一個不可變的類
- 這是最後一堂課
final
),且該類別的實例在建立後不能修改 ( immutable
)。這給了 String 類別幾個重要的優點:
-
由於不變性,String 類別實例的雜湊碼被快取。不需要每次都評估,因為物件的欄位值在創建後就永遠不會改變。當使用此類作為 的鍵時,這會提供高效能
HashMap
。 -
String 類別可以在多執行緒環境中使用,無需額外的同步。
-
String類別的另一個特點是它重載了
+
Java中的「」運算子。因此,字串的連接(相加)非常簡單:
public static void main(String[] args) {
String command = "Follow" + " " + "the" + " " + "white" + " " + "rabbit";
System.out.println(command); // Follow the white rabbit
}
在幕後,字串連接是由 StringBuilder 或 StringBuffer 類別(由編譯器決定)和一個方法append
(我們稍後將討論這些類別)執行的。如果我們將 String 類別的實例與其他類別的實例相加,後者將被簡化為字串表示形式:
public static void main(String[] args) {
Boolean b = Boolean.TRUE;
String result = "b is " + b;
System.out.println(result); //b is true
}
這是 String 類別的另一個有趣的屬性:任何類別的物件都可以使用toString()
類別中定義的方法轉換為字串表示形式Object
,並由所有其他類別繼承。通常,物件上的 toString() 方法會被隱含呼叫。例如,當我們在螢幕上顯示某些內容或將 String 新增到另一個類別的物件時。String 類別還有一項功能。Java 程式碼中定義的所有字串文字(例如「asdf」)都會在編譯時快取並添加到所謂的字串池中。如果我們運行以下程式碼:
String a = "Wake up, Neo";
String b = "Wake up, Neo";
System.out.println(a == b);
我們將在控制台中看到true,因為變數a
實際上b
引用在編譯時添加到字串池中的 String 類別的相同實例。即不會創建具有相同值的類別的不同實例,並且節省了記憶體。
缺陷:
不難猜測 String 類別主要用於處理字串。但在某些情況下,String類別的上述特性可能會從優點變成缺點。一旦在 Java 程式碼中建立了字串,通常會對它們執行許多操作:- 將字串轉換為不同的暫存器;
- 子串提取;
- 級聯;
- ETC。
public static void main(String[] args) {
String s = " Wake up, Neo! ";
s = s.toUpperCase();
s = s.trim();
System.out.println("\"" + s + "\"");
}
乍一看,我們似乎只是翻譯了“醒來,尼奧!”這句話。改為大寫,從此字串中刪除多餘的空格並將其用引號引起來。事實上,由於String類別的不可變性,每次操作都會建立新的字串實例並丟棄舊的字串實例,從而產生大量垃圾。如何避免記憶體浪費?
字串緩衝區類
要處理由於修改 String 物件而產生的臨時垃圾,可以使用 StringBuffer 類別。這是mutable
一個類,即 多變。StringBuffer 類別的物件可以包含一組特定的字符,可以透過呼叫某些方法來更改其長度和值。讓我們看看這個類別是如何運作的。若要建立新對象,請使用其建構函數之一,例如:
- StringBuffer() - 將建立一個空(無字元)對象
- StringBuffer(String str) - 將基於 str 變數建立一個物件(包含同一序列中 str 的所有字元)
StringBuffer sb = new StringBuffer();
StringBuffer sb2 = new StringBuffer("Not empty");
Java 中透過 StringBuffer 進行字串連線是使用append
. 一般來說,StringBuffer 類別中的方法append
被重載為幾乎可以接受任何資料類型:
public static void main(String[] args) {
StringBuffer sb = new StringBuffer();
sb.append(new Integer(2));
sb.append("; ");
sb.append(false);
sb.append("; ");
sb.append(Arrays.asList(1,2,3));
sb.append("; ");
System.out.println(sb); // 2; false; [1, 2, 3];
}
該方法append
傳回呼叫它的物件(像許多其他方法一樣),這允許在「鏈」中呼叫它。上面的例子可以這樣寫:
public static void main(String[] args) {
StringBuffer sb = new StringBuffer();
sb.append(new Integer(2))
.append("; ")
.append(false)
.append("; ")
.append(Arrays.asList(1,2,3))
.append("; ");
System.out.println(sb); // 2; false; [1, 2, 3];
}
StringBuffer 類別有許多處理字串的方法。讓我們列出主要的:
delete(int start, int end)
start
— 刪除從位置 開始、結束的字元子字串end
deleteCharAt(int index)
— 刪除位置處的字符index
insert(int offset, String str)
str
—在位置插入一行offset
。該方法insert
也是重載的並且可以採用不同的參數replace(int start, int end, String str)
- 將替換從一個位置start
到end
另一個位置的所有字符str
reverse()
— 反轉所有字元的順序substring(int start, int end)
start
- 將返回從一個位置開始到另一個位置的子字串end
substring(int start)
- 將傳回從位置開始的子字串
start
public static void main(String[] args) {
String numbers = "0123456789";
StringBuffer sb = new StringBuffer(numbers);
System.out.println(sb.substring(3)); // 3456789
System.out.println(sb.substring(4, 8)); // 4567
System.out.println(sb.replace(3, 5, "ABCDE")); // 012ABCDE56789
sb = new StringBuffer(numbers);
System.out.println(sb.reverse()); // 9876543210
sb.reverse(); // Return the original order
sb = new StringBuffer(numbers);
System.out.println(sb.delete(5, 9)); // 012349
System.out.println(sb.deleteCharAt(1)); // 02349
System.out.println(sb.insert(1, "One")); // 0One2349
}
優點:
-
如同已經提到的,StringBuffer 是一個可變類,因此使用它不會產生與 String 相同數量的記憶體垃圾。因此,如果對字串進行大量修改,最好使用
StringBuffer
. -
StringBuffer是一個線程安全的類別。它的方法是同步的,實例可以被多個執行緒同時使用。
缺陷:
一方面,線程安全是類別的優點,另一方面,它又是缺點。同步方法比非同步方法慢。這就是 StringBuilder 發揮作用的地方。讓我們弄清楚這是一個什麼樣的Java類別——StringBuilder,它有哪些方法以及它的特點是什麼。字串生成器類
Java中的StringBuilder是一個表示字元序列的類別。除了線程安全性之外,它在各方面都與 StringBuffer 非常相似。StringBuilder 提供了類似 StringBuffer 的 API。讓我們使用一個已經熟悉的範例來示範這一點,將變數宣告從 StringBufer 替換為 StringBuilder:public static void main(String[] args) {
String numbers = "0123456789";
StringBuilder sb = new StringBuilder(numbers);
System.out.println(sb.substring(3)); //3456789
System.out.println(sb.substring(4, 8)); //4567
System.out.println(sb.replace(3, 5, "ABCDE")); //012ABCDE56789
sb = new StringBuilder(numbers);
System.out.println(sb.reverse()); //9876543210
sb.reverse(); // Return the original order
sb = new StringBuilder(numbers);
System.out.println(sb.delete(5, 9)); //012349
System.out.println(sb.deleteCharAt(1)); //02349
System.out.println(sb.insert(1, "One")); //0One2349
}
唯一的區別是 StringBuffer 是線程安全的,並且它的所有方法都是同步的,而 StringBuilder 則不是。這是唯一的功能。Java中的StringBuilder由於方法的非同步性而比StringBuffer更快。因此,大多數情況下,除了多執行緒環境外,Java程式最好使用StringBuilder。我們在三個類別的比較表中總結了所有內容:
String、StringBuffer、StringBuilder
細繩 | 字串緩衝區 | 字串產生器 | |
---|---|---|---|
變動性 | Immutable (不) |
mutable (是的) |
mutable (是的) |
可擴展性 | final (不) |
final (不) |
final (不) |
線程安全 | 是的,由於不變性 | 是的,由於同步 | 不 |
何時使用 | 當使用很少被修改的字串時 | 在多執行緒環境中處理經常修改的字串時 | 在單線程環境中處理經常修改的字串時 |
GO TO FULL VERSION