finally 块的意义

Significance of finally block

Finally块在Try...[Catch]...Finally块中的意义是什么?

这不是代码吗

Resource r;
try{
  r = new Resource(); 
  r.methodThatThrowsException();
} catch (Exception e) {
  e.printStackTrace()
} finally {
  r.close()
}

相当于

Resource r;
try{
  r = new Resource(); 
  r.methodThatThrowsException();
} catch (Exception e) {
  e.printStackTrace()
}
r.close()

?如果它们具有相同的范围,我会理解,但事实上我必须在 try 块之外定义 Resource r 才能在 finally 块中使用它,这意味着我认为使用 finally 块没有任何优势。

我错过了什么吗?有没有我没有想到的特定情况需要 Finally 块?

在这种情况下,它们是等效的,因为 (a) 代码捕获任何异常,包括抛出的运行时异常,以及 (b) catch 块不会重新抛出异常,因此执行会继续。

finally 块通常用于在 (a) 或 (b) 不成立的情况下确保资源释放。在较新的 Java 实现中,您应该尽可能使用 try-with-resources

不,这不等价。在第一个代码段中 r.close() 始终被调用 ,而在第二个代码段中 r.close() 可能不会被调用:

  • 当 try 块抛出 Error(当您使用断言并且断言失败时可能发生的情况)
  • 当异常处理程序抛出另一个异常时

为确保资源始终被释放 close() 方法应从 finally 个块中调用。

这两个代码片段不同:如果异常处理块在另一个异常中结束,第二个代码片段将不会关闭。

这是一个例子:

public static void one() throws Exception {
    try {
        System.out.println("One");
        throw new Exception();
    } catch (Exception e) {
        System.out.println("Catch one");
        if (2 != 3) throw new Exception(); // "if" silences compiler's check
    } finally {
        System.out.println("Finally one");
    }
}
public static void two() throws Exception {
    try {
        System.out.println("Two");
        throw new Exception();
    } catch (Exception e) {
        System.out.println("Catch two");
        if (2 != 3) throw new Exception(); // "if" silences compiler's check
    }
    System.out.println("After two");
}

调用 one() 打印 Finally one,而 After two 永远不会打印 (demo 1)。

finally 块在您捕获特定异常 (blindly catching Exception is nearly always a bad idea) 时变得更加重要,因为 try 块可能会抛出您没有抛出的异常来绕过您的清理代码抓住。这是另一个例子:

public static void error() throws Exception {
    try {
        System.out.println("Try");
        throw new Error();
    } catch (Exception e) {
        System.out.println("Catch");
        throw new Exception();
    } finally {
        System.out.println("Finally");
    }
}

此代码打印 TryFinally,中间没有 Catch,因为 Error 没有被捕获在 catch 块中(demo 2).

不用说,如果您将清理代码放在 finally 块中,程序的人类读者将更容易找到您的清理代码。