扫描仪连续循环

时间:2015-11-05 18:52:24

标签: java

为什么我的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 个答案:

答案 0 :(得分:4)

  

为什么我的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's docs

  

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

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

但是你没有得到IllegalStateException,因为正如我在第一个问题的答案中所说,每次进入do-while循环时,你都会创建一个新对象。

修改

为什么你不能重新打开它?也来自Oracle的文档:

  

当扫描仪关闭时,如果是源,它将关闭其输入源   实现Closeable接口。

因此inputScanner.close()关闭System.in.

由于OutputStream关闭的一般合约(在this answer的帮助下):

public void close() throws IOException - >关闭此输入流并释放与此流关联的所有系统资源。 close的一般合同是它关闭输入流。封闭流无法执行输入操作,无法重新打开。

答案 1 :(得分:2)

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

  

抛出:
  IllegalStateException - 如果扫描程序已关闭

(强调我的)

所以要抛出IllegalStateException,首先需要关闭扫描程序,而不是从中读取数据的流。

让我们来看看这个例子:

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

最后一行抛出IllegalStateException因为你在关闭的扫描程序上调用hasNext方法(所以我们知道在调用它之后的sc.close()流也必须关闭,所以我们可以安全地假设没有更多元素可供阅读,或者因为流已关闭,我们可能无法阅读它。)

现在,如果我们不关闭扫描程序但关闭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并不会导致扫描程序抛出异常?

我怀疑在此处不抛出异常的决定是假设异常象征着代码问题。如果程序员允许扫描仪关闭,他还应该确保扫描仪的这个特定封闭实例不会在任何地方使用 现在返回false而不是抛出异常是正常反应,其中没有更多元素可供阅读。因此,如果扫描仪正在读取的流被自然关闭(就像我们读取文本文件并读取它的最后一行所以没有什么可读的)扫描仪处理这种情况就像正常一样(所以没有必要指出这是一些< em>特殊情况)。

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

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无法提供更多值。

追加陷阱

我之前没有提到过的一个问题是,如果输入错误,那么既不是"exit"也不是double,如果你没有从扫描器中消耗掉那个无效的缓存值使用nextXZYhasNext("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();
相关问题