Java Scanner.nextLine()不等待输入

4
我之前曾使用过几种方法来接受用户输入并将其以某一数据类型返回给调用该方法的方法。我在之前的项目中使用了这些方法或它们的变体。
我采用了我用于字符串的方法,选择给定的第一个字符并返回它,以便在菜单中使用。每次启动应用程序时,主菜单都会出现并等待用户输入。接收到此输入后,程序将持续循环直到停止。以下是我捕获字符的方法:
private char getUserChar(String prompt) {
    try {
        Scanner scan = new Scanner(System.in);
        System.out.print(prompt);
        String tempString = "";
        tempString = scan.nextLine();
        scan.close();
        char userChar = tempString.charAt(0);
        return userChar;
    } catch(Exception ex) {
        System.out.println(ex.getMessage());
    }
    return 0;
}

代码由于try/catch块而循环,因为scan.nextLine()从不等待下一个输入。如果没有它,异常通常与找不到新行有关。我尝试了while(scan.hasNextLine()),似乎对其他人有效;但是,一旦输入时间到达,它就永远无法跳出循环。我也不认为我会遇到每个人都遇到的nextInt()问题。我将在下面发布整个类的代码:
import java.util.Scanner;
import controller.Controller;
public class TUI {
    private Controller controller;
    public TUI() {
        Controller controller = new Controller();
        this.controller = controller;
    }

    public void run() {
        boolean wantToQuit = false;
        char userInput = 0;
        System.out.println("Welcome to the Mart.");
        do{
            userInput = mainMenu();
            if(isUserInputValid(userInput))
                switch(userInput){
                    case 'a': addItem();
                    break;
                    case 'r': controller.removeItem();
                    break;
                    case 'i': controller.printInventory();
                    break;
                    case 'p': controller.customerPurchase();
                    break;
                    case 'w': controller.weeklyStock();
                    break;
                    case 'c': wantToQuit = true;
                    break;
                }
            else System.out.println("\nMainMenu");



        } while(!(wantToQuit));
        System.out.println("WolfMart is now closed.  Thank you and good-bye.");
    }




    private boolean isUserInputValid(char userInput) {
        char[] testSet = {'a', 'r', 'i', 'p', 'c', 'w'};
        for(char currentChar : testSet) {
            if(currentChar == userInput)
                return true;
        }
        return false;
    }

    private char mainMenu() {
        System.out.println();
        controller.printInventory();
        String mainMenuSelection = "What would you like to do: (a)dd item, (r)emove item, print (i)nventory, " +
            "(p)urchase by customer, (c)lose store?\r\n";

        char mainMenuInput = getUserChar(mainMenuSelection);
        return mainMenuInput;
    }

    private char getUserChar(String prompt) {
        try {
            Scanner scan = new Scanner(System.in);
            System.out.print(prompt);
            String tempString = "";
            tempString = scan.nextLine();
            scan.close();
            char userChar = tempString.charAt(0);
            return userChar;
        } catch(Exception ex) {
            System.out.println(ex.getMessage());
        }
        return 0;
    }


    private int getUserInt(String prompt) {
        Scanner scan = new Scanner(System.in);
        int userInt = -1;
        try {
            System.out.print(prompt);
            String input = scan.nextLine();
            userInt = Integer.parseInt(input);
        }
        catch(NumberFormatException nfe) {
            System.out.println("I did not recognize your command, please try again.");
        }
        scan.close();
        return userInt;
    }

    private String getUserString(String prompt) {
        Scanner scan = new Scanner(System.in);
        String userString = null;
        try{
            System.out.print(prompt);
            userString = scan.nextLine();
        } catch(Exception ex)
        {
            System.out.println("I did not recognize your command, please try again.");
        }
        scan.close();
        return userString;
    }

    private double getUserDouble(String prompt) {
        Scanner scan = new Scanner(System.in);
        double userDouble = -1.0;
        try {
            System.out.print(prompt);
            String input = scan.nextLine();
            userDouble = Double.parseDouble(input);
        }
        catch(NumberFormatException nfe) {
            System.out.println("I did not recognize your command, please try again.");
        }
        scan.close();
        return userDouble;
    }

    private void addItem() {
        String itemName = "";
        double price;
        int quantity;
        String namePrompt = "Enter the name of the item being added to the inventory: ";
        String pricePrompt = "Enter the cost of " + itemName + ": ";
        String quantityPrompt = "Enter the quantity of " + itemName + ": ";
        itemName = getUserString(namePrompt);
        price = getUserDouble(pricePrompt);
        quantity = getUserInt(quantityPrompt);
        controller.addItem(itemName, quantity, price);
    }




}

1
每次关闭Scanner时,您都会关闭System.in。实际上,您只需要一个Scanner。 - obataku
1
哈哈...我真想踢自己的脸。我的上一个程序没有关闭它们,所以它们可以正常运行,但是这个新版本的Eclipse每次都要求我关闭它们。我想我会尝试重新修改一下,谢谢。 - floppsb
2个回答

9

正如我在我的评论中所述,问题在于每次像这样做时,您都会关闭System.in

Scanner scan = new Scanner(System.in);
...
scan.close();

现在,请看Scanner.nextLine的规范:

抛出异常:

  • NoSuchElementException - 如果未找到任何行
  • IllegalStateException - 如果该扫描器已关闭
现在,由于扫描器本身并没有关闭,因此不会抛出IllegalStateException。相反,如你之前提到的,将抛出另一个异常,"通常与未找到新行有关"——NoSuchElementException
假设您正在使用JDK 7,您可以通过查看Scanner.throwFor来了解其工作原理。
if ((sourceClosed) && (position == buf.limit()))
    throw new NoSuchElementException();

由于抛出了异常,getUserChar 返回了 0,然后在 run 循环中使用:

    do {
        userInput = mainMenu();
        if (isUserInputValid(userInput)) {
            ...
        } else {
            System.out.println("\nMainMenu");
        }
    } while (!wantToQuit);

由于输入无效,你被困在一个循环中不断打印"\nMainMenu\n"。
为了纠正这个问题,尝试仅使用一个Scanner,并且不要关闭System.in ;-)

哇,我原本以为可能只有一两行。回答得不错!1+ - Hovercraft Full Of Eels
谢谢你的解释。哈哈,我之前在调试时一直搞不明白为什么它会跳过所有东西,但现在这一切都变得非常清晰了。 - floppsb
哦我的天啊,我已经研究了一段时间,并且是使用了多个扫描仪,但关闭其中一个时,我就无法理解它。感谢您的这条评论。 - Amit Vig

0
请不要关闭 Scanner。
scan.close();    // Don't do it.

做那个会导致问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接