【问题标题】:Is it possible to use try with resources along with an input stream?是否可以将 try 与资源以及输入流一起使用?
【发布时间】:2020-01-18 00:23:52
【问题描述】:

当使用资源实现 try 时,我通过 try 语句的 () 内的 Scanner sc = new Scanner(System.in) 创建一个 Scanner 对象。 在 try 块中,我提示用户输入一个数值,通过sc.nextLine() 读取它并利用parseDouble 将其转换为方法。 如果最初输入的值无效,我会使用 do-while 循环重新提示用户输入值。

但是,如果用户输入了无效值,输入流将关闭,NumberFormatException 会被捕获,但在 do-while 循环的第二次迭代期间,会抛出“No line found”NoSuchElementException 并在此后无限期地抛出'流关闭' java.io.IOException。 有没有办法在使用资源尝试时规避这种情况?

public static void main(String[] args) {

  int x = 1;

  do {
      try (Scanner sc = new Scanner(System.in)) {
          System.out.print("Enter a numeric value: ");
          String input1 = sc.nextLine();
          Double d1;
          d1 = Double.parseDouble(input1);
          System.out.print("Enter a numeric value: ");
          String input2 = sc.nextLine();
          Double d2;
          d2 = Double.parseDouble(input2); 
          System.out.print("Choose an operation (+ - * /): ");
          String input3 = sc.nextLine();
          //sc.close();

          switch (input3) {
              case "+":
                  Double result = d1 + d2;
                  System.out.println("The answer is " + result);
                  break;
              case "-":
                  Double result1 = d1 - d2;
                  System.out.println("The answer is " + result1);
                  break;
              case "*":
                  Double result2 = d1 * d2;
                  System.out.println("The answer is " + result2);
                  break; 
              case "/":
                  Double result3 = d1 / d2;
                  System.out.println("The answer is " + result3);
                  break;
              default:
                  System.out.println("Unrecognized Operation!");
                  break;
          }
          x++;        
      } 
      catch (NumberFormatException e){ 
          System.out.println("Number formatting exception "+e.getMessage());
          System.out.println("Enter a proper value");    
      }
      catch (Exception e1){
          System.out.println("Arbitrary error encountered"+e1.getMessage());
      }
  }
  while(x==1);

}

【问题讨论】:

  • 它正在工作,但您不想在 in 循环中执行此操作;因为关闭Scanner 关闭System.in。将try 移到循环之外,您应该可以开始了。
  • 关闭已打开的资源是个好习惯。但是,Scanner 包装了您没有打开的标准输入。一般的经验法则是“不要关闭你没有打开的东西”。 JVM打开了stdin,所以留给JVM关闭吧。
  • 将 try 移到循环外将排除 catch 语句,如果输入了无效值,则会重新提示。

标签: java exception java.util.scanner inputstream try-with-resources


【解决方案1】:

正如 cmets 中的其他人已经说过的那样,您不应该将 try-with-resource 用于您自己没有打开的资源,尤其是 System.in,它不应该被应用程序关闭。

除此之外,您应该更喜欢预先测试条件,而不是捕获异常。此外,尽量避免代码重复,您最终会得到一个更简单的解决方案:

public static void main(String[] args) {
    Scanner sc = new Scanner(System.in).useDelimiter("\\R");
    System.out.print("Enter first numeric value: ");
    double d1 = getDouble(sc);
    System.out.print("Enter second numeric value: ");
    double d2 = getDouble(sc);
    System.out.print("Choose an operation (+ - * /): ");
    while(!sc.hasNext("[-+*/]")) {
      System.out.println(sc.next()+" is not a valid operation");
    }
    String operation = sc.next();

    double result;
    switch (operation) {
        case "+": result = d1 + d2; break;
        case "-": result = d1 - d2; break;
        case "*": result = d1 * d2; break; 
        case "/": result = d1 / d2; break;
        default:
            throw new AssertionError("should not happen due to pretest");
    }
    System.out.println(d1+operation+d2);
    System.out.println("The answer is " + result);
}    
private static double getDouble(Scanner sc) {
    while(!sc.hasNextDouble()) {
        System.out.println(sc.next()+" is not a number");
    }
    return sc.nextDouble();
}

此解决方案使用hasNextDouble() 询问扫描器下一个令牌是否具有双重格式,并且只有在确认后,应用程序才会通过nextDouble() 获取它。否则,它使用next() 来获取无效令牌,该令牌同时提供服务,报告并从未处理的输入中删除它。同样,hasNext("[-+*/]") 检查输入是否与支持的四个运算符中的一个匹配。

由于编译器不理解现在可能只出现四个有效输入中的一个,我们必须在switchdefault 情况下添加一个抛出语句,否则编译器会说@987654330 @ 可能尚未初始化。我们可以将result 初始化为默认值,但如果程序流程没有按预期工作,最好有一个强信号,而不是打印一个默认值作为错误结果。

Scanner 使用可配置的分隔符来决定什么构成标记,因此此解决方案使用useDelimiter("\\R") 使用换行符作为分隔符,将每一行视为一个标记,就像您的原始代码一样。因此,Pattern class 获取语法的完整描述。

【讨论】:

  • 有趣的解决方案。也可以使用 break 而不是在 switch 语句的默认情况下抛出断言错误,对吗?我认为抛出错误是最佳实践,即使它是一种冗余机制。是否存在可能违反预测试的情况?
  • 如果你只使用break,它只会留下switch 语句,试图继续。编译器会拒绝该可能的程序流,因为它会访问未初始化的result 变量。您可以改用return。但是,只要它不发生,你在一个永远不会发生的情况下放什么并不重要。当本应不可能的情况发生时,你想知道它,而不是默默地返回,不告诉发生了什么。如果发生这种情况,我们有一个错误,要么在我们这边,要么(不太可能但并非不可能)在编译器或 Scanner 类中
  • 嗯。如果未初始化结果变量,我假设 continue 会产生与 break 相同的效果?
  • continue 需要一个循环,在我的回答中,switch 语句周围没有循环。可以通过sc.next() 编写循环读取而无需预先测试,使用default: continue; 执行switch,然后使用result。从那时起,continue 将跳过result 的使用,它会起作用。
猜你喜欢
  • 1970-01-01
  • 2021-05-12
  • 2020-06-18
  • 2015-07-05
  • 2021-08-01
  • 2017-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-12
相关资源
最近更新 更多