try-finally 和 try-catch-finally 语句

try...catch...finally 语句将异常处理与清理代码结合在一起。finally 块包含将在所有情况下执行的代码。这使它们适用于资源管理和其他类型的清理。

尝试,终于

这是一个更简单(try...finally)形式的例子:

try {
    doSomething();  
} finally {
    cleanUp();
}

try...finally 的行为如下:

  • 执行 try 块中的代码。
  • 如果 try 块没有抛出异常:
    • 执行 finally 块中的代码。
    • 如果 finally 块抛出异常,则传播该异常。
    • 否则,控制传递到 try...finally 之后的下一个语句。
  • 如果 try 块中抛出了异常:
    • 执行 finally 块中的代码。
    • 如果 finally 块抛出异常,则传播该异常。
    • 否则,原始异常继续传播。

finally 块中的代码将始终执行。 (唯一的例外是如果调用 System.exit(int),或者如果 JVM 发生混乱。)因此,finally 块是始终需要执行的正确位置代码; 例如关闭文件和其他资源或释放锁。

try-catch-终于

我们的第二个例子展示了 catchfinally 如何一起使用。它还说明清理资源并不简单。

// This code snippet writes the first line of a file to a string
String result = null;
Reader reader = null;
try {
    reader = new BufferedReader(new FileReader(fileName));
    result = reader.readLine();
} catch (IOException ex) {
    Logger.getLogger.warn("Unexpected IO error", ex);  // logging the exception
} finally {
    if (reader != null) {
        try {
            reader.close();
        } catch (IOException ex) {
            // ignore / discard this exception
        }
    }
}

在这个例子中,try...catch...finally 的一整套(假设的)行为太复杂了,不能在这里描述。简单的版本是 finally 块中的代码将始终执行。

从资源管理的角度来看这个:

  • 我们在 try 块之前声明资源(即 reader 变量),以便它将在 finally 块的范围内。
  • 通过放置 new FileReader(...)catch 能够处理打开文件时抛出的任何 IOError 异常。
  • 我们需要在 finally 块中使用 reader.close(),因为我们无法在 try 块或 catch 块中拦截一些异常路径。
  • 但是,由于在初始化 reader 之前可能会抛出异常,我们还需要一个明确的 null 测试。
  • 最后,reader.close() 调用可能(假设)抛出异常。我们并不关心这一点,但如果我们不在源头捕获异常,我们需要在调用堆栈中进一步处理它。

Version >= Java SE 7

Java 7 及更高版本提供了另一种 try-with-resources 语法 ,可显着简化资源清理。