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

Java Scanner hasNextInt 導致無限迴圈問題:三種解決方案深度解析

     編輯:Java 相關知識

前言

使用 Scanner 處理使用者輸入時,hasNextInt() 搭配 nextInt() 是常見模式。但若使用者輸入非整數內容,若不正確處理緩衝區,**極易陷入無限迴圈**。本文深入剖析根因,提供三種解決方案並比較優劣。

一、問題重現:為什麼會無限迴圈?

Scanner sc = new Scanner(System.in);
while (true) {
    System.out.print("請輸入整數: ");
    if (sc.hasNextInt()) {
        int num = sc.nextInt();
        System.out.println("您輸入: " + num);
    } else {
        System.out.println("輸入錯誤,請重新輸入");
        // ❌ 缺少消耗錯誤 token,導致無限迴圈
    }
}

根因分析

  1. hasNextInt() 檢查緩衝區**下一個 token** 是否為整數。
  2. 若使用者輸入 abc,hasNextInt() 回傳 false。
  3. 進入 else 區塊,**但緩衝區內的 abc 並未被消耗**!
  4. 下一次迴圈 hasNextInt() 仍檢查同一個 abc,再次回傳 false。
  5. **無限迴圈形成**。

二、三種解決方案完整比較

方案一:每次迴圈建立新 Scanner (不推薦)

while (true) {
    Scanner sc = new Scanner(System.in); // 每次新建
    if (sc.hasNextInt()) { ... }
    else { System.out.println("錯誤"); }
    // sc 結束時自動關閉 System.in,後續無法再讀取!
}
優點缺點
邏輯簡單,無需手動清緩衝區嚴重缺陷:Scanner 關閉時會關閉 System.in,導致後續無法讀取任何輸入
效能差,頻繁建立物件
無法處理同行多輸入 (如 10 20 abc)

方案二:使用 next() 消耗單一錯誤 token (推薦單字輸入)

Scanner sc = new Scanner(System.in);
while (true) {
    System.out.print("請輸入整數: ");
    if (sc.hasNextInt()) {
        int num = sc.nextInt();
        System.out.println("您輸入: " + num);
    } else {
        String badToken = sc.next(); // ✅ 消耗掉錯誤 token
        System.out.println("無效輸入: '" + badToken + "',請輸入整數");
    }
}
優點缺點
精確消耗單一 token若使用者輸入 abc def 123,需迴圈 3 次才能清完錯誤輸入
保留同行後續合法輸入不適合「整行輸入驗證」場景

方案三:使用 nextLine() 消耗整行 (推薦整行驗證)

Scanner sc = new Scanner(System.in);
while (true) {
    System.out.print("請輸入整數: ");
    if (sc.hasNextInt()) {
        int num = sc.nextInt();
        sc.nextLine(); // ✅ 關鍵:消耗換行符,避免殘留影響下次 nextLine()
        System.out.println("您輸入: " + num);
    } else {
        String badLine = sc.nextLine(); // ✅ 一次消耗整行錯誤輸入
        System.out.println("無效輸入: '" + badLine + "',請輸入整數");
    }
}
優點缺點
一次清空整行,用戶體驗佳若同行有混合輸入 10 abc,10 會被 nextInt() 讀走,abc 連同換行被 nextLine() 讀走
符合「逐行輸入」直覺
避免換行符殘留導致後續 nextLine() 讀到空字串

三、進階最佳實踐:封裝通用輸入工具類

import java.util.Scanner;
import java.util.function.Predicate;

public class InputUtil {
    private static final Scanner SC = new Scanner(System.in);

    /** 通用整數輸入驗證 */
    public static int readInt(String prompt, Predicate validator, String errorMsg) {
        while (true) {
            System.out.print(prompt);
            if (SC.hasNextInt()) {
                int value = SC.nextInt();
                SC.nextLine(); // 消耗換行
                if (validator == null || validator.test(value)) {
                    return value;
                }
                System.out.println(errorMsg);
            } else {
                SC.nextLine(); // 清除整行無效輸入
                System.out.println("格式錯誤,請輸入整數");
            }
        }
    }

    /** 通用字串輸入 (非空) */
    public static String readNonEmptyString(String prompt) {
        while (true) {
            System.out.print(prompt);
            String line = SC.nextLine().trim();
            if (!line.isEmpty()) return line;
            System.out.println("不可為空,請重新輸入");
        }
    }

    // 使用範例
    public static void main(String[] args) {
        int age = readInt("請輸入年齡 (1-120): ", 
                          v -> v >= 1 && v <= 120, 
                          "年齡必須在 1-120 之間");
        String name = readNonEmptyString("請輸入姓名: ");
        System.out.printf("歡迎 %s,您 %d 歲%n", name, age);
    }
}

四、常見陷阱總結表

現象原因解決
無限迴圈印出錯誤訊息未消耗緩衝區錯誤 token在 else 區塊呼叫 next() 或 nextLine()
nextLine() 讀到空字串前一次 nextInt() 留下換行符nextInt() 後立即呼叫 nextLine() 清除換行
關閉 Scanner 後無法再讀取Scanner.close() 會關閉 System.in全程使用單一 Scanner 實例,程式結束前勿關閉
混合輸入 10 abc 行為怪異nextInt() 只讀 10,abc 留在緩衝區統一用 nextLine() 讀整行,再用 Integer.parseInt() 解析

五、替代方案:完全避開 Scanner 緩衝區問題

若專案允許,建議使用 BufferedReader 讀整行再解析,邏輯最清晰:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class SafeInput {
    private static final BufferedReader BR = new BufferedReader(new InputStreamReader(System.in));

    public static int readInt(String prompt) throws IOException {
        while (true) {
            System.out.print(prompt);
            String line = BR.readLine();
            try {
                return Integer.parseInt(line.trim());
            } catch (NumberFormatException e) {
                System.out.println("請輸入有效整數");
            }
        }
    }
}

結語

Scanner 的緩衝區機制是導致無限迴圈的核心原因。**黃金法則:每次 hasNextXxx() 失敗時,必須在 else 區塊呼叫 next() 或 nextLine() 消耗輸入。** 方案三 (nextLine()) 最符合人類逐行輸入直覺,推薦作為預設處理模式;封裝通用工具類可大幅減少重複錯誤。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved