首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

《Thinking in Java》讀書筆記:第 13 章 字符串——不可變性、StringBuilder、格式化、正則表達式、Scanner

     編輯:Java 相關知識

字符串是 Java 應用中使用最頻繁的類型,第 13 章深入剖析 String 不可變設計、拼接性能陷阱、格式化輸出、正則表達式引擎、Scanner 掃描器。本文系統整理核心知識與實戰最佳實踐。

String 不可變性:設計哲學與影響

  • 不可變:String 所有「修改」方法(substring、replace、toUpperCase、concat)均返回新對象,原對象不變。
  • 好處:線程安全(無狀態)、可安全共享、哈希值可緩存(hashCode 懶加載)、字符串常量池去重、安全性(網絡/文件路徑參數不可被竄改)。
  • 代價:頻繁修改產生大量臨時對象,GC 壓力大。

拼接性能:+ 操作符 vs StringBuilder

// 循環中用 + → 每次迭代隱式 new StringBuilder() → append → toString() → 極慢
String s = "";
for (int i = 0; i < 10000; i++) s += i; // O(n²) 字符複製

// 正確:顯式 StringBuilder
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10000; i++) sb.append(i);
String result = sb.toString(); // O(n)
  • 編譯器優化:單行 a + b + c 會自動優化為 StringBuilder,但循環/條件分支中無法優化。
  • JDK 5 前用 StringBuffer(同步、慢),JDK 5+ 推薦 StringBuilder(非同步、快)。
  • toString() 中循環拼接必須顯式用 StringBuilder。

格式化輸出:printf / format / Formatter

核心語法

System.out.printf("姓名:%s,年齡:%d,身高:%.2f%n", name, age, height);
// %[參數索引$][標誌][寬度][.精度]轉換符

常用轉換符

三種格式化 API

  • System.out.printf(format, args):直接輸出控制台。
  • String.format(format, args):返回格式化字符串(靜態方法,內部用 Formatter)。
  • new Formatter().format(format, args):底層引擎,可輸出到任意 Appendable(StringBuilder、文件、Socket)。

正則表達式:字符串處理的瑞士軍刀

String 內置便捷方法

"abc123def".matches("\\d+")        // 整體匹配:false
"abc123def".split("\\d+")          // 分割:["abc", "def"]
"abc123def".replaceAll("\\d+", "#") // 替換:"abc#def"
"abc123def".replaceFirst("\\d+", "#") // 首次替換:"abc#def"

Pattern & Matcher:高性能、複雜場景

Pattern p = Pattern.compile("(\\d{4})-(\\d{2})-(\\d{2})"); // 編譯一次,多次復用
Matcher m = p.matcher("2024-01-15");
while (m.find()) {
    System.out.println("年:" + m.group(1) + " 月:" + m.group(2) + " 日:" + m.group(3));
    // group(0) 整個匹配,group(1..n) 捕獲組
}

量詞模式對比

轉換符對應類型示例
%s任意 (toString)"hello"
%d整數42
%f浮點數3.14159
%.2f保留 2 位小數3.14
%x / %X十六進制2a / 2A
%tX日期時間組合%tF → 2024-01-15
%n平台換行符Windows \r\n / Linux \n

分組與邊界

  • (...) 捕獲組,(?:...) 非捕獲組(不佔 group 索引)。
  • m.start()/m.end() 獲取匹配區間,m.group(i) 獲取組內容。
  • matches() 整體匹配,lookingAt() 前缀匹配,find() 部分匹配(可多次調用遍歷)。

Scanner:掃描器統一輸入解析

Scanner sc = new Scanner(System.in); // 或 new Scanner(file) / new Scanner(string)
sc.useDelimiter("\\s+"); // 自定義分隔符(正則),默認空白字符

while (sc.hasNextInt()) {
    int num = sc.nextInt();
    System.out.println("讀到整數:" + num);
}
sc.close();
  • nextInt()、nextDouble()、nextLine()、next(Pattern) 等類型安全讀取。
  • 分隔符僅作用於下一個 token,若模式包含分隔符永不會匹配。
  • 關閉 Scanner 會關閉底層 Readable(如 System.in 將不可再讀),慎用。

實戰避坑指南

  1. 字符串常量池:String s1 = "a"; String s2 = "a"; s1 == s2 → true;new String("a") 強制創建堆對象,== 比較引用,內容比較永遠用 equals()。
  2. 正則轉義:Java 字符串中 \ 需雙重轉義 \\,Pattern.quote(str) 自動轉義字面量。
  3. Scanner 陷阱:nextInt() 後緊跟 nextLine() 會讀到空行(換行符未消費),解決:sc.nextLine() 先消費換行。
  4. 性能敏感場景:預編譯 Pattern、復用 Matcher、避免循環中 String.matches() 重複編譯。

總結

字符串處理核心三板斧:不可變認知 + StringBuilder 拼接 + 正則/格式化 API 選型。掌握 Pattern/Matcher 高級用法(分組、邊界、量詞模式),能解決 99% 文本解析需求。Scanner 適合簡單控制台/文件掃描,複雜解析請上正則或專用解析器(Jackson、Gson、ANTLR)。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
模式量詞行為示例輸入匹配結果
貪婪* + ? {n,m}盡可能多匹配"aaab" / a*b"aaa"
勉強*? +? ?? {n,m}?盡可能少匹配"aaab" / a*?b"" (空串)
佔有*+ ++ ?+ {n,m}+不回溯,失敗即失敗"aaab" / a*+b失敗(佔有所有 a)