一、为什么使用Try-With-Resources
在 Java 7 之前,一个常见的例子是 java.io.FileInputStream 。要正确使用它,你必须编写一些棘手的样板代码:
// exceptions/MessyExceptions.java
import java.io.*;
public class MessyExceptions {
public static void main(String[] args) {
InputStream in = null;
try {
in = new FileInputStream(
new File("MessyExceptions.java"));
int contents = in.read();
// Process contents
} catch(IOException e) {
// Handle the error
} finally {
if(in != null) {
try {
in.close();
} catch(IOException e) {
// Handle the close() error
}
}
}
}
}
幸运的是, Java 7 引入了 try-with-resources 语法,它可以非常清楚地简化上面的代码:
// exceptions/TryWithResources.java
import java.io.*;
public class TryWithResources {
public static void main(String[] args) {
try(
InputStream in = new FileInputStream(
new File("TryWithResources.java"))
) {
int contents = in.read();
// Process contents
} catch(IOException e) {
// Handle the error
}
}
}
在 Java 7 之前,try 后面总是跟着一个 {,但是现在可以跟一个带括号的定义 ——这里是我们创建的 FileInputStream 对象。括号内的部分称为 资源规范头(resource specification header) 。现在 in 在整个 try 块的其余部分都是可用的。更重要的是,无论你如何退出 try 块(正常或通过异常),和以前的 finally 子句等价的代码都会被执行,并且不用编写那些杂乱而棘手的代码。
二、Try-with-resources 是如何工作的?
try-with-resources 定义子句中创建的对象(在括号内)必须实现 java.lang.AutoCloseable 接口,这个接口只有一个方法:close() 。当在 Java 7 中引入 AutoCloseable 时,许多接口和类被修改以实现它;查看 Javadocs 中的 AutoCloseable,可以找到所有实现该接口的类列表,其中包括 Stream 对象:
// exceptions/StreamsAreAutoCloseable.java
import java.io.*;
import java.nio.file.*;
import java.util.stream.*;
public class StreamsAreAutoCloseable {
public static void main(String[] args) throws IOException{
try(
Stream<String> in = Files.lines(
Paths.get("StreamsAreAutoCloseable.java"));
PrintWriter outfile = new PrintWriter(
"Results.txt"); // [1]
) {
in.skip(5)
.limit(1)
.map(String::toLowerCase)
.forEachOrdered(outfile::println);
} // [2]
}
}
[1] 你在这里可以看到其他的特性:
资源规范头中可以包含多个定义,并且通过分号进行分割(最后一个分号是可选的)。
规范头中定义的每个对象都会在 try 语句块运行结束之后调用 close() 方法。
[2] try-with-resources 里面的 try 语句块可以不包含 catch 或者 finally 语句而独立存在。
在这里,IOException 被 main() 方法抛出,所以这里并不需要在 try 后面跟着一个 catch 语句块。
三、Try-with-resources细节
为了研究 try-with-resources 的基本机制,我们将创建自己的 AutoCloseable 类:
// exceptions/AutoCloseableDetails.java
class Reporter implements AutoCloseable {
String name = getClass().getSimpleName();
Reporter() {
System.out.println("Creating " + name);
}
public void close() {
System.out.println("Closing " + name);
}
}
class First extends Reporter {}
class Second extends Reporter {}
public class AutoCloseableDetails {
public static void main(String[] args) {
try(
First f = new First();
Second s = new Second()
) {
}
}
}
输出为:
Creating First
Creating Second
Closing Second
Closing First
退出 try 块会调用两个对象的 close() 方法,并以 与创建顺序相反的顺序关闭它们 。顺序很重要,因为在这种情况下,Second 对象可能依赖于 First 对象,因此如果 First 在第__Second __关闭时已经关闭。 Second 的 close() 方法可能会尝试访问 First 中不再可用的某些功能。