为什么我的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,请不要关闭它(如果关闭,我们可以重新打开并从中读取任何数据,因此例外)“。为什么会这样?
答案 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,请不要关闭它(如果是的话) 关闭,我们无法重新打开它并从中读取任何数据,因此例外)
“扫描仪执行后尝试执行搜索操作 关闭将导致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
,如果你没有从扫描器中消耗掉那个无效的缓存值使用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();