当try和finally块都抛出异常时,try-catch-finally块的行为?

时间:2011-10-13 18:25:31

标签: java

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并关闭两个抛出异常,则方法readFirstLineFromFileWithFinallyBlock抛出finally块抛出的异常;从try块抛出的异常被抑制。为什么这样的行为呢?为什么try块的异常被压制?

6 个答案:

答案 0 :(得分: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块中,它的Exception会传播出方法,并传播给调用方法。

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

如果资源声明本身抛出异常会发生什么,我没有安装JDK7,所以我不确定。为什么不将以下代码放在一个测试项目中(与它看起来完全一样,带有伪造的路径),看看会发生什么,然后告诉我们结果对每个人都有什么好处:

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

答案 1 :(得分:1)

  

为什么这样的行为?为什么try块出现异常   supressed?

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

答案 2 :(得分:0)

因为函数只能抛出一个异常。后来的exceptiobn压制了所有的前任

答案 3 :(得分:0)

那么,你想要什么样的行为?没有同时抛出多个异常的概念 - 因此其中一个例外必须“赢”。如果有一种一致的方式来表示语言中的“多个事情已经出错”(除了将一个例外包装在另一个例外之外),那将是很好的,但船只在那条船上航行。

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

编辑:请注意,在Java 7中,try-with-resources功能有更多选项。有关详细信息,请参阅the documentation;它详细介绍了这种具体情况。

答案 4 :(得分:0)

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

最后,由于这个原因,块总是应该非常防御地编码。参见:

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

答案 5 :(得分:0)

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

注释: “该评论包含一个资源尝试声明。你能解释当两个抛出异常时会发生什么吗?”

答案: 它将为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