【问题标题】:Java using scanner with try-with-resourcesJava 使用带有 try-with-resources 的扫描仪
【发布时间】:2018-05-15 06:46:38
【问题描述】:

我有两个版本的 Java 代码可以获取用户输入,直到用户键入“q” 版本 1:

public class Test {
    public static void main(String[] args) {
        String input = "";
        while (!input.equals("q")) {
            Scanner scanner = new Scanner(System.in);
            System.out.print("Input: ");
            input = scanner.nextLine();
            System.out.println("Input was: " + input);
        }
    }
}

版本 2:

public class Test {
    public static void main(String[] args) {
        String input = "";
        while (!input.equals("q")) {
            try(Scanner scanner = new Scanner(System.in)){
                System.out.print("Input: ");
                input = scanner.nextLine();
                System.out.println("Input was: " + input);
            }
        }
    }
}

第 1 版按预期工作,但第 2 版无法按预期工作。 也就是第一次读取用户输入后,会产生错误

Input: 12
Input was: 12Exception in thread "main" 
Input: java.util.NoSuchElementException: No line found
    at java.util.Scanner.nextLine(Scanner.java:1540)
    at RealEstateCompany.main(RealEstateCompany.java:115)

我的猜测是因为版本 2 使用 try with resource 所以它在使用后关闭扫描仪并导致错误?

提前感谢您的帮助!

[更新] 版本 3:

public class Test {
    public static void main(String[] args) {
        String input = "";
        try(Scanner scanner = new Scanner(System.in)){
            while (!input.equals("q")) {
                System.out.print("Input: ");
                input = scanner.nextLine();
                System.out.println("Input was: " + input);
            }
        }
    }
}

版本 3 有效。但是,为什么版本3可以,而版本2不行呢?

【问题讨论】:

  • try-with 自动关闭底层Scanner,即自动关闭底层流,即System.in。之后,您将无法再获得任何用户输入。
  • 您是否尝试将 try(Scanner scanner = new Scanner(System.in)) 排除在 while 循环之外?
  • @RajuSharma 谢谢你的建议。是的,它解决了我的问题,但为什么这能解决问题?
  • 版本 3 有效,因为当您进入 while 循环时,您仍然在 try 块内。仅当您离开 try 时,资源才会关闭,这是您的第二个示例在一次迭代后发生的情况。
  • @QBrute 谢谢你的回答!我对 Java 还很陌生,所以你能告诉我更多细节吗?

标签: java try-catch try-with-resources


【解决方案1】:

我运行了一些测试并将 catch 块添加到您的代码中。这是代码

public static void main(String[] args) {
    String input = "";
    while (!input.equals("q")) {
        try(Scanner scanner = new Scanner(System.in)){
            System.out.print("Input: ");
            input = scanner.nextLine();
            System.out.println("Input was: " + input);
        }
        catch (Exception e) {
            e.printStackTrace();
        }            
    }
}

添加catch块时,有2种结果 1,仅输入 q,按预期工作 2、输入任何其他String,异常

Input: java.util.NoSuchElementException: No line found
at java.util.Scanner.nextLine(Scanner.java:1585)
at rews.pub.Test.main(Test.java:11)

添加catch块后,我们会看到程序不会停止,因为while循环

这是另一个更简单的测试

public class Test {
public static void main(String[] args) {
    String input = "";
    Scanner scanner = new Scanner(System.in);
    System.out.println("inout--1---");
    input = scanner.nextLine();
    scanner.close();

    Scanner scanner2 = new Scanner(System.in);
    System.out.println("inout--2---");
    input = scanner2.nextLine();
    scanner2.close();

}

}

同样的异常

inout--1---
11
inout--2---
Exception in thread "main" java.util.NoSuchElementException: No line  found
at java.util.Scanner.nextLine(Scanner.java:1585)
at rews.pub.Test.main(Test.java:15)

这是我的看法。 在第一次运行结束时,try()block 将关闭块中的资源,意味着我们关闭了 system.in system.in 是 inputSteam 的一个对象,而 system.in 是 final 和 static,我们不能像 'new Scanner(System.in)' 那样再次打开它

【讨论】:

    【解决方案2】:

    为我的 cmets 添加更多细节

    try-with 块定义如下:

    try(...) {
       ...
    }
    

    括号中的参数需要是java.lang.AutoCloseable 的实例。例如java.io.InputStream 类,它也是System.in 的类。

    一旦块离开,try-with 会尝试自动关闭其提供的资源。根据使用的资源,它也会关闭所有它自己的子资源。

    以你的例子,你有try(Scanner scanner = new Scanner(System.in)),它使用Scanner作为资源。扫描仪本身使用System.in 作为资源。一旦离开try 块(到达} 时),它就会尝试关闭其资源,即Scanner 实例。此实例还尝试关闭 资源,System.in

    一旦System.in 关闭,您将无法再从控制台获得任何输入(我认为至少不能通过一些额外的工作......)。

    具体来说,在你的第二个例子中:

    while (!input.equals("q")) {
        try(Scanner scanner = new Scanner(System.in)){
                ...
        }  // <--- The block is left, scanner is closed, System.in is closed
    } // <-- start a new iteration
    

    仅经过一次迭代,System.in 就关闭了。当然,您在下一次迭代中创建了一个新的 Scanner,但 System.in 仍然关闭,这就是您在这种情况下遇到异常的原因。

    你的第三个例子:

    try(Scanner scanner = new Scanner(System.in)){
        while (!input.equals("q")) {
            ...
        } // <-- start a new iteration, while still in the same try block
    } // <-- only after the while, your resources are closed
    

    你正在循环你的while,而还在里面try。因此,在您离开 whiletry 之前,不会关闭任何资源。这意味着,Scanner 保持不变,System.in 保持不变。这使您可以继续从控制台读取,直到完成循环。

    【讨论】:

    • 非常感谢您提供最清晰的解释。现在我对使用 try-with-resources 感觉好多了!
    【解决方案3】:

    试试这个:

       String input = "";
       try (Scanner scanner = new Scanner(System.in)) {
           while (!input.equals("q")) {
               System.out.print("Input: ");
               input = scanner.nextLine();
               System.out.println("Input was: " + input);
           }
       }
    

    您可以在 try-with-resources 中使用每个实现 CloseableAutoCloseable 的类,当代码到达 try 调用的末尾时,它会调用我们示例中的 Scanner 类的 close() 函数。

    【讨论】:

    • 这基本上不是 OPs 帖子中的第 3 版吗?
    • @Gal 感谢您的回答和解释。我现在唯一不明白的是为什么版本 2 不起作用。我知道在 try close() 结束时调用并关闭了扫描仪,但在第二次迭代中,在 try(Scanner scanner = new ...) 它重新创建了新的扫描仪实例,所以我认为它应该能够读取输入再来一次。
    • 投反对票,因为答案没有回答 OP 的问题。
    猜你喜欢
    • 2013-07-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多