【问题标题】:Why is try-with-resources catch block selectively optional?为什么 try-with-resources catch 块是选择性可选的?
【发布时间】:2014-10-19 06:16:18
【问题描述】:

我读到 try-with-resources 中的 catch 块是可选的。 我尝试在 try-with-resources 块中创建 Connection 对象,没有后续的 catch 块,只是为了从 eclipse 中获取编译器错误: “由自动 close() 调用引发的未处理的异常类型 SQLException。”

由于可以在 try-with-resources 中使用的每个资源都实现了AutoCloseable,因此在调用close() 方法时可能会引发异常,我不明白catch 子句如何是可选的,鉴于它不允许我跳过从close() 捕获异常。

是否有特殊要求AutoCloseable的具体实现不直接声明其close()方法中抛出的异常? (例如,用不会抛出任何异常的close() 覆盖AutoCloseableclose() throws Exception)?

..或者这可能只是一个日食问题?

编辑:这是仍然触发问题的最简单的代码片段:

try (Connection con = dataSource.getConnection()) {
  /*...*/

}

关于这是否与使用 JNDI 数据源有关的想法?

提前致谢。

【问题讨论】:

  • 尝试在没有Eclipse的命令行下编译
  • 您可以提供一个不会抛出异常的AutoClosable 实现,在这种情况下您不需要捕获任何东西,或者您可以在您的方法签名中添加一个throws 子句,在这种情况下您不需要catch 子句。
  • 感谢 Edwin 的回答和关于包含方法抛出的注释。我忘记了这样做会使 try 语句不必捕获异常。

标签: java try-with-resources autocloseable


【解决方案1】:

您可以通过声明 AutoClosable 的 close() 方法而无需任何异常或带有 RuntimeException 来创建不需要显式捕获块的 AutoClosable。没有任何异常,很明显不需要 catch-block。此外,编译器不会静态检查要捕获的 RuntimeException(与已检查的异常相反)。

例子:

public class AutoClosableDemo
{

    public static void main( final String[] args )
    {
        try (MyAutoCloseable1 mac1 = new MyAutoCloseable1())
        {
            System.out.println( "try-with-resource MyAutoCloseable1" );
        }
        try (MyAutoCloseable2 mac2 = new MyAutoCloseable2())
        {
            System.out.println( "try-with-resource MyAutoCloseable2" );
        }
        // The following is not allowed, because
        // "Unhandled exception type Exception thrown by automatic close() invocation on mac3"
        // try (MyAutoCloseable3 mac3 = new MyAutoCloseable3())
        // {
        // System.out.println( "try-with-resource MyAutoCloseable13" );
        // }
        System.out.println( "done" );
    }

    public static class MyAutoCloseable1 implements AutoCloseable
    {
        @Override
        public void close()
        {
            System.out.println( "MyAutoCloseable1.close()" );
        }
    }

    public static class MyAutoCloseable2 implements AutoCloseable
    {
        @Override
        public void close() throws RuntimeException
        {
            System.out.println( "MyAutoCloseable2.close()" );
        }
    }

    public static class MyAutoCloseable3 implements AutoCloseable
    {
        @Override
        public void close() throws Exception
        {
            System.out.println( "MyAutoCloseable3.close()" );
        }
    }
}

【讨论】:

    【解决方案2】:

    您可以检查 JLS,但实际上有一个相对简单的推理,为什么这是该语言应该表现的唯一正确方式。

    检查异常的主要规则是任何由方法声明的检查异常都必须被处理,要么捕获它,要么让调用方法抛出它。

    try-with-resources 总是(隐式地)调用 close 方法。

    因此,如果您使用的 AutoClosable 的特定关闭方法(由 try 中声明的类型确定)声明抛出一个检查异常,例如 SQLException,那么您确实需要在某个地方处理这个检查异常,否则有可能违反规则!

    如果 close 方法 not 声明它抛出一个已检查的异常,则不违反规则,并且您不需要处理已检查的异常以隐式调用 close 方法。如果您确实尝试捕获从未声明为抛出的已检查异常,则实际上是编译失败。

    【讨论】:

      【解决方案3】:

      如果close() 不能抛出已检查的异常,这是可选的。但是,如果close() 可以,则需要以正常方式处理已检查的异常,或者使用catch 块,或者从try-with-resources 块所在的方法中抛出。

      更多详情在JLS 14.2.3

      14.20.3.2。扩展的资源尝试

      带有至少一个 catch 子句和/或 finally 子句的 try-with-resources 语句称为扩展的 try-with-resources 语句。

      扩展的try-with-resources语句的含义:

      try ResourceSpecification
          Block
      [Catches]
      [Finally]
      

      由以下对嵌套在 try-catch 或 try-finally 或 try-catch-finally 语句中的基本 try-with-resources 语句的翻译给出:

      try {
          try ResourceSpecification
             Block
      }
      [Catches]
      [Finally]
      

      翻译的效果是将资源规范放在“内部”try 语句中。这允许扩展的 try-with-resources 语句的 catch 子句捕获由于任何资源的自动初始化或关闭而导致的异常。

      此外,在 finally 块执行时,所有资源都已关闭(或试图关闭),这与 finally 关键字的意图保持一致。

      关于这是否与使用 JNDI 数据源有关的想法?

      是的,是的。

      在您提供的示例 try-with-resourses 块中,有必要捕获异常并处理,或者从块所在的方法中抛出,因为SQLException 是检查异常。

      【讨论】:

      • 感谢您的详细解答。我很惊讶,因为我认为 try-with-resources 是为了捕获 try 中指定为 try 子句“内部”的异常而构建的。我现在看到 AutoCloseable 的实现必须覆盖 close() 以免抛出可选的异常,因为 try-with-resources 不会执行此隐式捕获。
      • @Mer 语言规范始终是解决这类问题的好资源。
      【解决方案4】:

      您可能只是抛出异常(或在另一个 try-catch 块中捕获它):

      private static void test() throws IOException {
          try(InputStream is = new FileInputStream("test.txt")) {
              while(is.read() > -1) {
              }
          } finally {
              // Will get executed, even if exception occurs
              System.out.println("Finished");
          }
      }
      

      【讨论】:

        【解决方案5】:

        并非每个 Java 类 (!) 都会引发异常。有时您只想使用 try-with-resources 来使用自动关闭功能,而不是别的。

        BufferedReader br = new BufferedReader(new FileReader(path));
        try {
            return br.readLine();
        } finally {
            if (br != null) br.close();
        }
        

        这个 catch 是可选的,因为 readLine() 不会抛出(检查的)异常。

        是的,close() 可能会引发异常,但 try-with-resources 也可以处理。

        try (BufferedReader br = new BufferedReader(new FileReader(path))) {
            return br.readLine();
        } 
        

        所以这个 try-with-resources 不需要捕获。

        【讨论】:

        • 我不明白。此示例不使用 try-with-resources。
        • 我展示了 try-with-resources 替换的代码。它也没有 catch,这就是为什么 try-with-resources 必须将 catch 设为可选的原因。
        • 您创建资源的地方(在本例中为缓冲区读取器)确实可能会引发 IOException。如果您使用了 try-with-resources,则需要有一个 catch
        • 不过,这与 OP 的问题无关。 catch 在这里仍然是可选的。
        • 您继续而不证明它是可选的。在您使用它的地方显示整个函数签名。无论您是否使用 try-with-resources,您都必须声明 throws 子句或执行 catch。
        猜你喜欢
        • 1970-01-01
        • 2014-12-18
        • 2015-05-05
        • 2017-08-03
        • 2015-03-27
        • 2013-07-18
        • 1970-01-01
        • 2014-12-09
        • 2020-12-02
        相关资源
        最近更新 更多