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



这个问题让我很困惑。我写了一个从循环内部调用的方法。第一次它完美运行,之后它只是挂起。

这是方法:

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 时,我可以在没有根文件夹的情况下输入一些东西,它会一直询问,直到它得到根文件夹。那么为什么它在第二次调用时不这样做呢?

这是怎么回事?

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

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

问题是步骤 4。 关闭Scanner时,也会关闭System.in

下次打电话promptUser

  1. 创建另一个包装System.inScanner。 此时System.in已关闭。
  2. 调用hashNext(),立即返回false...因为Scanner包裹的流是封闭的。
  3. 重复 2.恶心。 你有一个无限循环。

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


但是有必要在某个时候关闭扫描仪吗?

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

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

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

System.in的情况下,没有要写入的数据。 由于您无法再次打开System.in(因为您不知道它连接到什么(,因此最多可以"泄漏"一个文件描述符,因此这不是问题。

Eclipse警告我应该这样做。

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

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

您应该将其更改为如下所示的内容:

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

阅读 hasNext() 的文档

如果此扫描程序的输入中有另一个标记,则返回 true。此方法可能会在等待输入扫描时阻止。扫描仪不会前进超过任何输入。返回:当且仅当此扫描程序具有另一个令牌时为 true

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

最新更新