【问题标题】:Behaviour of try-catch-finally block when exceptions are thrown from both try and finally block?当try和finally块都抛出异常时try-catch-finally块的行为?
【发布时间】:2011-10-13 18:25:30
【问题描述】:
static String readFirstLineFromFileWithFinallyBlock(String path) throws IOException {
  BufferedReader br = new BufferedReader(new FileReader(path));
  try {
    return br.readLine();
  } finally {
    if (br != null) br.close();
  }
}

如果方法 readLine 和 close 都抛出异常,则方法 readFirstLineFromFileWithFinallyBlock 抛出从 finally 块抛出的异常;从 try 块抛出的异常被抑制。为什么会有这样的行为?为什么 try 块的异常被抑制了?

【问题讨论】:

  • 因为 JLS 是这么说的。将close() 放在另一个try-catch 中,您在其中忽略或仅记录异常。
  • 第一个异常会作为原因添加到第二个吗?

标签: java


【解决方案1】:

仅处理 try 部分中的异常。除此以外的任何内容(包括 finally 部分都未包含在 try 中,因此不处理异常。

如果你想捕捉/抑制 finally 块内的异常,你需要用它自己的 try/catch 块包装 if (br != null) br.close();,像这样:

...
} finally {
  try {
    if (br != null) br.close();
  } catch (Exception e) {
    // whatever handling
  }
}
...

此外,try 块的异常被抑制,因为这是 try 块的行为 - 尝试一些事情并给你一个恢复的机会。由于您在 try 块之后没有捕获任何异常,因此不会运行任何代码来响应它。

然后,无论是否抛出任何异常,都会执行finally 块。如果它抛出一个异常,并且因为它不在它自己的 try/catch 块中,它的异常会传播到方法之外,并传播到调用方法。

以您下面的评论为例,从 Java 7 开始,您需要参考 the documentation outlined here,并关注倒数第二个标题为“抑制的异常”的部分,这基本上表示可以从try 块中抛出多个异常,每个声明的资源最多一个异常。

至于如果资源声明本身抛出异常会发生什么,我没有安装JDK7,所以我不确定。为什么不把下面的代码放在一个测试项目中(和它看起来完全一样,用一个虚假的路径),看看会发生什么,然后告诉我们它的结果是什么,对大家都有好处:

try (BufferedReader br = new BufferedReader(new FileReader("a totally invalid path"))) {
  return br.readLine();
}

【讨论】:

  • try (BufferedReader br = new BufferedReader(new FileReader(path))) { return br.readLine();如果尝试使用资源和 br.readline 抛出异常怎么办。哪个会被抑制,为什么?
  • 您可以编辑您的第一条评论(或添加一条新评论)吗?您的括号/括号不正确,因此,我不确定我是否遵循您的要求。
  • 该注释包含一个 try-with-resources 语句。你能解释一下当两者都抛出异常时会发生什么吗?
  • 哦,我明白了。我以前在 JDK6 上工作时不熟悉“try-with-resources”结构,而且它似乎直到 JDK7 才存在。我回答了你的问题(大部分),并发布了关于该主题的官方文档的参考。帮我们一个忙,如果你安装了 JDK7(我没有),那么试试我最后给出的代码示例,然后告诉我们:会发生什么,抛出什么异常,并将其与你安装时发生的情况进行比较在其中放置一个有效路径(到现有文件)。
【解决方案2】:

为什么会有这样的行为?为什么 try 块的异常 被压制了?

  1. 因为这是架构定义它的方式。
  2. 因为数十年的经验表明,这是解决潜在歧义的最佳解决方案。

【讨论】:

    【解决方案3】:

    因为一个函数只能抛出一个异常。后面的异常抑制了前面的所有

    【讨论】:

      【解决方案4】:

      根据规范,它只能抛出一个异常,“finally”块中的那个获胜。

      出于这个原因,最后应该始终对块进行非常防御性的编码。见:

      Is a finally block without a catch block a java anti-pattern?

      【讨论】:

        【解决方案5】:

        嗯,你想要什么行为?没有一次抛出多个异常的概念 - 因此其中一个异常必须“获胜”。如果语言中有某种一致的方式来表示“多处出错”,那就太好了(支持不仅仅是将一个异常包装在另一个异常中),但船就靠那个航行了。

        这也是 Guava 有 Closeables.closeQuietly 的原因之一,因此原来的异常不会被抑制。

        编辑:请注意,在 Java 7 中,try-with-resources 功能提供了更多选项。更多详情请见the documentation;它详细介绍了这种特定情况。

        【讨论】:

        【解决方案6】:

        当我查看这篇文章时,有人在评论中添加了与下面相同的问题。我做了一些分析,这是我的答案。

        评论: “该评论包含一个 try-with-resources 语句。你能解释一下当两者都抛出异常时会发生什么吗?”

        回答: try 块会抛出异常,try-with-resource 语句抛出的异常被抑制。

        这是一个快速的示例程序,可以帮助您理解这一点。

        class MyResource implements AutoCloseable {
            public void close() throws SQLException {
                throw new SQLException();
            }
        }
        
        public class Try {
        
            public static void main(final String[] args) {
                try(MyResource mr = new MyResource ()) {
        
                    System.out.println("Hi");
                    throw new IOException();
        
                } 
                catch (IOException | SQLException e) {
                    System.out.println("Exception raised:" + e.getClass());
                    System.out.println("Exception suppressed:" + e.getSuppressed()[0]);
        
                }
            }
        }
        

        这个程序的输出:

        引发异常:类 java.io.IOException

        异常被抑制:java.sql.SQLException

        【讨论】:

          猜你喜欢
          • 2017-10-03
          • 2013-02-23
          • 2014-01-08
          • 2012-09-20
          • 2018-06-15
          • 2015-09-05
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多