一、什麼是String?¶
在Java中,String是用於表示文本數據的類,它是不可變的(一旦創建,內容就無法修改)。正因爲不可變,String在Java中被廣泛用於存儲和處理文本,比如用戶輸入、文件名、URL等。
二、String的創建方式¶
Java中創建String對象主要有兩種方式,它們的內存行爲不同,初學者容易混淆,需要重點區分。
1. 直接賦值(推薦)¶
String s1 = "hello";
String s2 = "hello";
- 原理:Java會先在字符串常量池(一種特殊的內存區域,用於複用相同的字符串常量)中查找是否存在
"hello"。 - 如果存在,
s1和s2直接指向常量池中的同一個"hello"對象。 - 如果不存在,會在常量池中創建新的
"hello",並讓s1/s2指向它。 - 特點:相同常量會被複用,節省內存。例如,
s1和s2雖然是兩個變量,但它們的引用相同(s1 == s2爲true)。
2. new關鍵字創建¶
String s3 = new String("hello");
String s4 = new String("hello");
- 原理:無論常量池是否有
"hello",都會在堆內存中創建一個新的String對象,內容爲"hello",且新對象的引用與常量池無關。 - 特點:即使內容相同,
s3和s4的引用也不同(s3 == s4爲false)。因爲new會強制新建堆對象,而直接賦值優先複用常量池。
對比直接賦值與new創建¶
| 方式 | 引用是否相同(相同內容) | 內存位置 | 是否複用常量池 |
|---|---|---|---|
| 直接賦值 | true(常量池複用) |
常量池 | 是 |
| new創建 | false(堆中新建) |
堆內存 | 否(但內容相同的常量會先放入常量池) |
三、String的拼接操作¶
拼接字符串是常用操作,Java提供了多種方式,需注意不同方式的性能和結果差異。
1. 使用+號拼接(最直觀)¶
String a = "a";
String b = "b";
String c = a + b; // 結果:"ab"
- 原理:
+號在編譯時會被編譯器優化。 - 如果拼接的是全常量(如
"a" + "b"),會直接在常量池生成"ab"。 - 如果拼接中包含變量(如
a + b,a/b是變量),會在堆中創建新的String對象。 - 注意:頻繁用
+號拼接(尤其是循環中)會創建大量臨時對象,效率極低!
2. 使用concat()方法¶
String d = "a".concat("b"); // 結果:"ab"
- 原理:
concat()是String類的方法,返回拼接後的新字符串,原字符串不變(因爲String不可變)。 - 示例:
String e = "hello";
String f = e.concat("world"); // f是"helloworld",e仍爲"hello"
3. 使用StringBuilder/StringBuffer(高效拼接)¶
String不可變,每次拼接都會新建對象;而StringBuilder/StringBuffer是可變的字符序列,拼接時直接修改內部數組,效率更高。
- 區別:
- StringBuilder:非線程安全,單線程下效率更高(默認推薦)。
- StringBuffer:線程安全(方法加了synchronized),多線程場景使用。
示例:
StringBuilder sb = new StringBuilder();
sb.append("a"); // 拼接單個字符
sb.append("b"); // 拼接字符串
String result = sb.toString(); // 轉換爲String(最終結果)
四、String的比較操作¶
比較字符串時,==和equals()的作用完全不同,初學者常因混淆兩者導致邏輯錯誤。
1. ==:比較對象引用(是否指向同一個對象)¶
String s1 = "hello";
String s2 = "hello";
String s3 = new String("hello");
System.out.println(s1 == s2); // true(指向常量池同一個對象)
System.out.println(s1 == s3); // false(s3是堆中新建對象,引用不同)
2. equals():比較字符串內容(忽略引用)¶
String s1 = "hello";
String s3 = new String("hello");
System.out.println(s1.equals(s3)); // true(內容相同)
- 注意:
equals()只能用於非null字符串,否則會拋出NullPointerException(如null.equals("a"))。- 避免用
==代替equals()比較內容!
3. 正確比較空字符串¶
判斷字符串是否爲空時,推薦用isEmpty()或length() == 0,而非== ""或== null。
String s = "";
System.out.println(s.isEmpty()); // true(空字符串,但非null)
System.out.println(s.length() == 0); // true
System.out.println(s == ""); // true(內容相同,但不推薦)
五、常見問題與解決方法¶
1. 混淆==和equals()¶
錯誤場景:
String a = new String("hello");
String b = new String("hello");
if (a == b) { // 錯誤!==比較引用,a和b是不同對象
System.out.println("a和b相同");
} else {
System.out.println("a和b內容相同但不是同一個對象");
}
解決:用equals()比較內容:
if (a.equals(b)) { // 正確!比較內容
System.out.println("a和b內容相同");
}
2. 循環中頻繁用+號拼接字符串¶
錯誤場景:
String result = "";
for (int i = 0; i < 1000; i++) {
result = result + i; // 每次循環新建String對象,效率極低!
}
解決:用StringBuilder替代+號:
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
sb.append(i); // 直接修改內部數組,無額外對象創建
}
String result = sb.toString(); // 最終轉換爲String
3. 空字符串判斷用== ""¶
錯誤場景:
String s = null;
if (s == "") { // 錯誤!null不能用== ""判斷
System.out.println("空字符串");
}
解決:先判空,再比較內容:
if (s != null && s.isEmpty()) { // 推薦寫法
System.out.println("空字符串");
}
六、總結¶
- 不可變性:
String一旦創建無法修改,任何拼接/修改操作都會生成新對象。 - 創建方式:直接賦值優先複用常量池,
new關鍵字強制新建堆對象。 - 拼接選擇:少量拼接用
+或concat(),大量拼接(如循環)用StringBuilder。 - 比較規則:
==比較引用,equals()比較內容,避免混淆;空字符串用isEmpty()或length() == 0,並先判null。
掌握以上內容,就能避免大部分String相關的常見錯誤,寫出高效清晰的Java代碼。