一、String 核心特性
- 不可變性:String 對象一旦創建,內容不能改變,所有「修改」操作實際返回新對象
- 常量池機制:字面量創建的字符串存放在字符串常量池,相同內容共享引用
- 底層實現:JDK 9+ 使用
byte[] + coder(Latin-1/UTF-16),早期版本用 char[]
1.1 創建方式差異演示
String s1 = "hello"; // 常量池
String s2 = "hello"; // 同引用
System.out.println(s1 == s2); // true
String s3 = new String("hello"); // 堆內存新對象
String s4 = new String("hello"); // 另一個新對象
System.out.println(s3 == s4); // false
System.out.println(s3.equals(s4)); // true(比較內容)
1.2 字符數組構造
char[] data = {'s','u','n',' ','j','a','v','a'};
String str1 = new String(data); // "sun java"
String str2 = new String(data, 4, 4); // "java" (偏移量4,長度4)
二、核心方法分類速查
2.1 獲取與查找
| 方法 | 說明 | 示例結果 |
charAt(int index) | 獲取指定索引字符 | "abc".charAt(1) → 'b' |
length() | 字符串長度 | "abc".length() → 3 |
indexOf(String str) | 首次出現索引,無則-1 | "ababa".indexOf("ba") → 1 |
lastIndexOf(String str) | 最後出現索引 | "ababa".lastIndexOf("ba") → 3 |
contains(CharSequence s) | 是否包含子串 | "abc".contains("b") → true |
2.2 比較與判斷
String a = "Java", b = "java";
a.equals(b); // false,區分大小寫
a.equalsIgnoreCase(b); // true,忽略大小寫
a.startsWith("Ja"); // true
a.endsWith("va"); // true
a.compareTo(b); // 負數,按字典序比較
2.3 修改與轉換(返回新字符串)
String s = " Hello World ";
s.trim(); // "Hello World" 去首尾空格
s.toLowerCase(); // " hello world "
s.toUpperCase(); // " HELLO WORLD "
s.replace("World", "Java"); // " Hello Java " 替換所有匹配
s.replaceFirst("\\s+", ""); // "Hello World " 正則替換首個
s.substring(2); // "Hello World " 從索引2到末尾
s.substring(2, 7); // "Hello" 索引2~6
2.4 分割與連接
"a,b,c".split(","); // ["a","b","c"] 正則分割
String.join("-", "a","b"); // "a-b" 靜態連接方法(JDK8+)
2.5 格式化與轉換
String.format("%.2f", 3.14159); // "3.14"
Integer.toString(100, 16); // "64" 進制轉換
"100".chars().forEach(System.out::println); // 字符流處理(JDK8+)
三、性能優化建議
- 頻繁拼接使用
StringBuilder(單線程)或 StringBuffer(線程安全)
- 避免在循環中使用
+ 拼接,會產生大量臨時對象
- 大字符串去重可用
String.intern() 手動入池(慎用,常量池內存有限)
- 正則分割/替換預編譯
Pattern 復用
四、常見面試陷阱
// 輸出什麼?
String s = "a" + "b" + "c"; // 編譯期優化為 "abc",1個對象
String s2 = "a";
s2 += "b"; // 運行期 StringBuilder,2個對象
new String("abc") == "abc"; // false,堆 vs 常量池