在 Java 開發中,從控制台或文件讀取輸入是基礎且高頻的操作。java.util.Scanner 與 java.io.BufferedReader 是兩大主流選擇,但它們的設計理念、適用場景與潛在陷阱截然不同。本文將通過實戰代碼對比、核心差異表格化總結,助你精準選型並避開經典坑點。
這是無數初學者踩過的坑。請先看下面這段代碼:
// Scanner 版本
import java.util.Scanner;
public class ScannerTrap {
public static void main(String[] args) {
Scanner scn = new Scanner(System.in);
System.out.print("請輸入整數:");
int a = scn.nextInt(); // 讀取整數,留下換行符在緩衝區
System.out.print("請輸入字符串:");
String b = scn.nextLine(); // 直接讀到殘留的換行符,返回空字符串
System.out.printf("結果:%d 和 %s%n", a, b);
}
}
輸入:50\nGeek
輸出:請輸入整數:請輸入字符串:結果:50 和
字符串 "Geek" 被跳過了!原因在於 nextInt() 等 nextXXX() 方法不會消耗換行符,而 nextLine() 專門讀取「直到換行符為止」的內容,結果讀到了殘留的換行符直接返回空值。
scn.nextLine() 專門消耗換行符。nextLine() 讀取整行,再用 Integer.parseInt() 轉型。BufferedReader,從根源規避該問題。// BufferedReader 版本
import java.io.*;
public class BRDemo {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.print("請輸入整數:");
int a = Integer.parseInt(br.readLine()); // 讀整行並轉型
System.out.print("請輸入字符串:");
String b = br.readLine(); // 直接讀下一行
System.out.printf("結果:%d 和 %s%n", a, b);
}
}
輸出:結果:50 和 Geek —— 完全符合預期。
| 維度 | Scanner | BufferedReader | 建議 |
|---|---|---|---|
| 線程安全 | 非線程安全 | 同步方法,線程安全 | 多線程環境首選 BufferedReader |
| 緩衝區大小 | 1 KB 字符緩衝 | 8 KB 字節緩衝 | 大數據量讀取 BufferedReader 優勢明顯 |
| 解析能力 | 內建正則解析基本類型 | 僅讀原始文本,需手動轉型 | 簡單交互用 Scanner;高性能批量讀取用 BufferedReader |
| 執行速度 | 較慢(含解析開銷) | 較快(純文本讀取) | 性能敏感場景選 BufferedReader |
| 場景 | 推薦類 | 關鍵代碼片段 |
|---|---|---|
| 控制台簡單交互 | Scanner | new Scanner(System.in).nextInt() |
| 多線程讀取同一輸入源 | BufferedReader | synchronized(br) { br.readLine(); } |
| 讀取超大文本文件(GB 級) | BufferedReader | try (var br = new BufferedReader(new FileReader(f))) { String line; while ((line=br.readLine())!=null) {…} } |
| 需按正則分割 Token | Scanner | sc.useDelimiter("\\s*,\\s*") |
理解 nextXXX() 與 nextLine() 的換行符語義差異,是駕馭 Scanner 的鑰匙;而 BufferedReader 憑藉線程安全、大緩衝、零解析開銷,成為高併發、大數據場景的不二之選。實際項目中,建議工具類封裝統一入口,根據業務層需求靈活切換實現,做到心中有數、代碼無憂。
參考來源: GeeksforGeeks - Difference between Scanner and BufferReader Class in Java