try-with-resources 语句
Version >= Java SE 7
正如 try-catch-final 语句示例所示,使用 finally 子句进行资源清理需要大量的样板代码才能正确实现边缘情况。Java 7 以 try-with-resources 语句的形式提供了一种更简单的方法来处理这个问题。
什么是资源?
Java 7 引入了 java.lang.AutoCloseable 接口,允许使用 try-with-resources 语句管理类。实现 AutoCloseable 的类的实例称为资源。这些通常需要及时处理,而不是依靠垃圾收集器来处理它们。
AutoCloseable 接口定义了一个方法:
public void close() throws Exception
close() 方法应以适当的方式处理资源。规范声明在已经处理的资源上调用该方法应该是安全的。另外,强烈建议实现 Autocloseable 的类声明 close() 方法抛出比 Exception 更具体的异常,或者根本没有例外。
各种标准 Java 类和接口实现了 AutoCloseable。这些包括:
InputStream,OutputStream及其子类Reader,Writer及其子类Socket和ServerSocket及其子类Channel及其子类,和- JDBC 接口
Connection,Statement和ResultSet及其子类。
应用程序和第三方类也可以这样做。
基本的 try-with-resource 语句
try-with-resources 的语法基于经典的 try-catch , try-finally 和 try-catch-finally 形式。这是一个基本形式的例子; 即没有 catch 或 finally 的形式。
try (PrintStream stream = new PrintStream("hello.txt")) {
stream.println("Hello world!");
}
在 try 子句之后,要管理的资源在 (...) 部分中声明为变量。在上面的例子中,我们声明了一个资源变量 stream 并将其初始化为新创建的 PrintStream。
资源变量初始化后,执行 try 块。完成后,将自动调用 stream.close() 以确保资源不会泄漏。请注意,无论块如何完成,都会发生 close() 调用。
增强的 try-with-resource 语句
在试穿与资源语句可以用 catch 和 finally 块增强,与 Java 的前 7 的 try-catch-终于语法。下面的代码片段为我们之前的代码片段添加了一个 catch 块来处理 PrintStream 构造函数可以抛出的 FileNotFoundException:
try (PrintStream stream = new PrintStream("hello.txt")) {
stream.println("Hello world!");
} catch (FileNotFoundException ex) {
System.err.println("Cannot open the file");
} finally {
System.err.println("All done");
}
如果资源初始化或 try 块抛出异常,则将执行 catch 块。finally 块将始终执行,就像传统的 try-catch-finally 语句一样。
但有几点需要注意:
- 资源变量超出了
catch和finally块的范围。 - 资源清理将在语句尝试匹配
catch块之前发生。 - 如果自动资源清理引发异常,则可以在其中一个
catch块中捕获。
管理多个资源
上面的代码段显示了一个被管理的资源。事实上, try-with-resources 可以在一个语句中管理多个资源。例如:
try (InputStream is = new FileInputStream(file1);
OutputStream os = new FileOutputStream(file2)) {
// Copy 'is' to 'os'
}
这表现得像你期望的那样。is 和 os 都在 try 块结束时自动关闭。有几点需要注意:
- 初始化发生在代码顺序中,后来的资源变量初始化程序可以使用先前的值。
- 将清除已成功初始化的所有资源变量。
- 资源变量按其声明的相反顺序进行清理。
因此,在上面的例子中,is 在 os 之前被初始化并且在它之后被清理,并且如果在初始化 os 时存在异常则将清除 is。
try-with-resource 和经典 try-catch-finally 的等价性
Java 语言规范根据经典的 try-catch-finally 语句指定 try-with-resource 表单的行为。 (有关详细信息,请参阅 JLS。) **
例如,这个基本的资源尝试 :
try (PrintStream stream = new PrintStream("hello.txt")) {
stream.println("Hello world!");
}
被定义为等同于此 try-catch-finally :
// Note that the constructor is not part of the try-catch statement
PrintStream stream = new PrintStream("hello.txt");
// This variable is used to keep track of the primary exception thrown
// in the try statement. If an exception is thrown in the try block,
// any exception thrown by AutoCloseable.close() will be suppressed.
Throwable primaryException = null;
// The actual try block
try {
stream.println("Hello world!");
} catch (Throwable t) {
// If an exception is thrown, remember it for the finally block
primaryException = t;
throw t;
} finally {
if (primaryException == null) {
// If no exception was thrown so far, exceptions thrown in close() will
// not be caught and therefore be passed on to the enclosing code.
stream.close();
} else {
// If an exception has already been thrown, any exception thrown in
// close() will be suppressed as it is likely to be related to the
// previous exception. The suppressed exception can be retrieved
// using primaryException.getSuppressed().
try {
stream.close();
} catch (Throwable suppressedException) {
primaryException.addSuppressed(suppressedException);
}
}
}
(JLS 指定实际的 t 和 primaryException 变量对于普通的 Java 代码是不可见的。)
增强形式的 try-with-resources 被指定为与基本形式的等价。例如:
try (PrintStream stream = new PrintStream(fileName)) {
stream.println("Hello world!");
} catch (NullPointerException ex) {
System.err.println("Null filename");
} finally {
System.err.println("All done");
}
相当于:
try {
try (PrintStream stream = new PrintStream(fileName)) {
stream.println("Hello world!");
}
} catch (NullPointerException ex) {
System.err.println("Null filename");
} finally {
System.err.println("All done");
}