提问者:小点点

扫描仪连续循环


为什么我的inputScanner在第一次输入后没有阻塞?它进入连续循环。忽略此代码的其他细节。

public class Test {
    public static void main(String[] args) {

        boolean finished;

        do {
            Scanner inputScanner = new Scanner(System.in);
            finished = inputScanner.hasNext("exit");
            boolean validNumber = inputScanner.hasNextDouble();
            if (validNumber) {
                double number = inputScanner.nextDouble();

                System.out.print(number);
            } else if (!finished) {
                System.out.println("Please try again.");
            }
            inputScanner.close();
        } while (!finished);
    }
}

编辑:在与此相关的上一篇文章中,提到“因此,如果您要稍后使用 System.in 请不要关闭它(如果它已关闭,我们无法重新打开它并从中读取任何数据,因此例外)”。为什么会这样?


共2个答案

匿名用户

为什么我的inputScanner在第一次输入后没有阻塞?

因为每次进入循环时都在创建一个新的扫描仪,所以第一次迭代中的对象与第二次及以后的迭代中的对象不同

public class Test {
    public static void main(String[] args) {

        boolean finished;

        do {
            Scanner inputScanner = new Scanner(System.in); //Here you're making a new instance of inputScanner each time you come to this line after the do-while loop ends.
            finished = inputScanner.hasNext("exit");
            boolean validNumber = inputScanner.hasNextDouble();
            if (validNumber) {
                double number = inputScanner.nextDouble();

                System.out.print(number);
            } else if (!finished) {
                System.out.println("Please try again.");
            }
            inputScanner.close();
        } while (!finished);
    }
}

如果您希望它被“阻止”或“关闭”,请将此行移动到do{行之前。

Scanner inputScanner = new Scanner(System.in);

对于第二个问题:

因此,如果您要使用 System.in 以后不要关闭它(如果它已关闭,我们无法重新打开它并从中读取任何数据,因此异常)

来自Oracle的文档:

"在扫描仪关闭后尝试执行搜索操作将导致IllegalStateException "

这就像试图让一个死人做某事,就像僵尸一样!(Java讨厌僵尸!D:

但是您不会得到那个< code > IllegalStateException ,因为正如我在回答您的第一个问题时所说的,每次进入< code>do-while循环时,您都在创建一个新的对象。

你为什么不能重新打开它?另请参阅Oracle的文档:

关闭扫描程序时,如果源实现了可关闭接口,它将关闭其输入源。

因此inputScanner.close关闭System.in.

并且由于OutputStream的关闭的一般契约(借助于这个回答):

public void off()抛出IOException--

匿名用户

如果您查看扫描仪方法的文档,如hasNext(String)hasNextDouble,您将看到

抛出:< br> IllegalStateException -如果此扫描仪关闭

(强调我的)

因此,要抛出 IllegalStateException,您首先需要关闭 Scanner,而不是从中读取数据的流。

让我们来看看这个例子:

Scanner sc = new Scanner(System.in);

System.out.println("type something:");
System.out.println(sc.hasNext());// true
System.out.println("your data: "+ sc.nextLine());

sc.close();

System.out.println("type something:");
System.out.println(sc.hasNext());// throws java.lang.IllegalStateException: Scanner closed

最后一行抛出<code>IllegalStateException</code>,因为您正在关闭的扫描程序上调用<code>hasNext</code〕方法(所以我们知道,在调用<code<sc.close()</code}之后,它从中读取的流也必须关闭,所以我们可以安全地假设没有更多的元素可以读取,或者由于流已关闭,我们可能不允许读取它)。

现在,如果我们不关闭扫描仪,而是关闭System.in,我们仍然可以使用这个扫描仪实例而不会出现异常。所以让我们简单地将sc.close();更改为System.in.close()(为了简单起见,我将跳过异常处理):

Scanner sc = new Scanner(System.in);

System.out.println("type something:");
System.out.println(sc.hasNext());// true
System.out.println("your data: "+ sc.nextLine());

System.in.close();

System.out.println("type something:");
System.out.println(sc.hasNext());// false

正如你所看到的,这里没有例外,因为它不是扫描仪被关闭,而是流哪个扫描仪被读取。

为什么关闭 System.in 不会导致扫描程序引发异常?

我怀疑这里不抛出异常决定是基于这样的假设,即异常象征着代码有问题。如果程序员允许扫描器被关闭,他也应该确保扫描器的这个关闭的实例不会在任何地方被使用。< br >现在返回< code>false,而不是抛出exception,这是正常反应,因为没有更多元素要读取。因此,如果扫描器正在读取的流被自然关闭(就像我们读取文本文件并读取它的最后一行,所以没有更多要读取的内容),扫描器会像正常情况一样处理这种情况(所以没有必要指出这是某种异常情况)。

现在,在循环中,您可以将这两种情况结合起来。您的代码可以简化为:

Scanner sc = new Scanner(System.in);

System.out.println("type something:");
System.out.println(sc.hasNext());// true
System.out.println("your data: "+ sc.nextLine());

System.in.close();
sc = new Scanner(System.in);//IMPORTANT

System.out.println("type something:");
System.out.println(sc.hasNext());// false

正如您在行中看到的

sc = new Scanner(System.in);//IMPORTANT

您正在创建尚未关闭的扫描仪的新实例,因此其hasXYZ方法总是返回false,因为System.in无法提供更多值。

附加存水弯

我之前没有提到的一个问题是,在输入错误的情况下,如果您没有使用任何 nextXZY 方法(如 hasNext(“exit”)或 hasNextDouble 从扫描程序中消耗无效的缓存值,则既不是“退出”也不是双倍,这将仍然基于该无效数据,例如:

Scanner sc = new Scanner("foo 1");
System.out.println(sc.hasNextInt());//false because `foo` is not integer
System.out.println(sc.hasNextInt());//also false because we are still 
                                    //reading `foo` which is not integer
String str = sc.next();//lets read (sonsume) foo
System.out.println(sc.hasNextInt());//true since 1 is integer

解决办法

解决此类问题的最简单方法是仅创建一个 Scanner 实例,该实例将处理 System.in 并在整个应用程序中重用它。然后在应用程序结束时,您可以决定关闭扫描仪或 System.in

所以你的代码看起来像:

boolean finished;
Scanner inputScanner = new Scanner(System.in);
do {
    finished = inputScanner.hasNext("exit");
    boolean validNumber = inputScanner.hasNextDouble();
    if (validNumber) {
        double number = inputScanner.nextDouble();

        System.out.print(number);
    } else if (!finished) {
        System.out.println("Please try again.");
        inputScanner.next();// lets not forget to consume from scanner cached
                            // invalid data which is neither double or "exit"
    }
} while (!finished);
inputScanner.close();