Java System.in hasNext 方法仅在第一次调用时有效

Java System.in hasNext method works only first time it is called

这个问题难倒我了。我写了一个从循环内部调用的方法。第一次完美运行,之后就挂了。

这是方法:

public static String promptUser(){
    String path = "";
    Scanner reader = new Scanner(System.in);  

    while (!path.contains(ROOT_FOLDER)){
        System.out.println(String.format("Please paste in the directory path from WinSCP, including %s: ", ROOT_FOLDER));

        while (true) {
            if (reader.hasNext()){
                path = reader.nextLine();
                break;
            }
        }
    }
    reader.close();
    return path;
}

这就是循环

        while (true) {
        try {
            listFiles(promptUser());
            break;
        } 
        catch (Exception e) {
            e.printStackTrace();
            System.err.println(e.getMessage());
        }
    }

我第一次 运行 它会多次提示用户获取我们需要的信息(目录路径)。然后它向 FTP 服务器发送请求,如果该路径不存在,我希望它继续提示用户。但是我的调试器告诉我在第二轮它只是挂起:

        if (reader.hasNext()){ 

再多的回车键也无法继续。在第一次调用 promptUser 时,我可以输入没有根文件夹的内容,它会一直询问,直到它获得根文件夹。那么为什么它在第二次调用时不这样做呢?

这是怎么回事?

获取下一个元素时,不应在循环内使用 break。这会导致您的 while(true) 循环退出。这意味着您获得第一个项目并完成。

你应该把它改成这样:

while(reader.hasNext()) {
    path = reader.nextLine();
    // do something with the path here...
}

阅读 hasNext()

的文档

Returns 如果此扫描器在其输入中有另一个标记,则为真。此方法可能会在等待输入扫描时阻塞。扫描仪不会前进超过任何输入。 Returns: 当且仅当此扫描器有另一个令牌时才为真

所以,hasNext() 正在等待您输入一些值。它会等到找到用户输入的单个字符串。

第一次调用 promptUser 时,它会执行以下操作:

  1. 创建一个 Scanner 包装 System.in
  2. 调用 hashNext,它会阻塞直到有数据要读取。
  3. 读取一行。
  4. 关闭Scanner.
  5. Return行。

问题出在第 4 步。当您关闭 Scanner 时,您 也会 关闭 System.in

下次你打电话给 promptUser

  1. 创建另一个包裹 System.inScannerSystem.in此时关闭。
  2. 立即调用 hashNext() returns false ... 因为 Scanner 包裹的流已关闭。
  3. 重复 2. 令人作呕。你有一个无限循环。

解决方案:不要关闭 System.in 或包裹它的 Scanner。并且不要只创建第二个 Scanner 来包装 System.in。相反,重复使用原来的 Scanner


But is it necessary to close the scanner at some point?

您永远不需要关闭包装(原始)System.in 流的 Scanner。我们关闭流的原因是:

  • 导致写入缓冲数据(对于输出流),并且
  • 避免文件描述符泄漏。

泄漏文件描述符的问题是进程(即您的 JVM)一次只能打开一定数量的文件。如果您泄漏文件描述符,您可能会发现尝试打开文件开始失败并出现意外 IOExceptions。

System.in的情况下没有要写入的数据。因为你不能再打开System.in(因为你不知道它连接的是什么)最多一个文件描述符可以是"leaked",所以这不是问题。

Eclipse warns that I should.

关于关闭扫描器等的 Eclipse 启发式方法相当简单。在这种情况下,警告是 可能 误报,但我需要查看代码才能确定。