Try-with-resources 是 Java7 出现的一个新的异常处理机制,它能够很容易地关闭在 try-catch 语句块中使用的资源。传统的关闭资源方式是利用 Try-Catch-Finally 管理资源(旧的代码风格) 即在 Java7 以前程序中使用的资源需要被明确地关闭。无论是使用文件 IO 流,还是网络 Socket 流,都免不了调用 close() 将流关闭。如果需要操作的流过多,就会导致混乱。一旦忘记将关闭方法放到 finally 中,很有可能出现流未被关闭,占用大量内存空间的问题。
- try-catch-finally 方式如下:
AC ac = null; AC2 ac2 = null; try { ac = new AC(); ac2 = new AC2(); } catch (Exception e) { } finally { ac.close(); ac2.close(); }Copy to clipboardErrorCopied
- try-with-resources 的语法如下:
try (AC ac = new AC(); AC2 ac2 = new AC2()) { } catch (Exception e) { }Copy to clipboardErrorCopied
可以很明显的看到,try-with-resources 会自动调用类中的 close() 方法,简化了流程,提高了代码的整洁度。
IO 案例
private static void printFile() throws IOException {
InputStream input = null;
try{
input = new FileInputStream("d:\\hello.txt");
int data = input.read();
while(data != -1){
System.out.print((char) data);
data = input.read();
}
} finally {
if(input != null){
input.close();
}
}
}Copy to clipboardErrorCopied
以上程序 try 语句块中有 3 处能抛出异常,finally 语句块中有一处会抛出异常。以上程序 try 语句块中有 3 处能抛出异常,finally 语句块中有一处会抛出异常。不论 try 语句块中是否有异常抛出,finally 语句块始终会被执行。这意味着,不论 try 语句块中发生什么,InputStream 都会被关闭,或者说都会试图被关闭。如果关闭失败,close()方法也可能会抛出异常。
假设 try 语句块抛出一个异常,然后 finally 语句块被执行。同样假设 finally 语句块也抛出了一个异常。那么哪个异常会根据调用栈往外传播?即使 try 语句块中抛出的异常与异常传播更相关,最终还是 finally 语句块中抛出的异常会根据调用栈向外传播。在 Java7 以后,对于上面的例子可以用 try-with-resource 结构这样写:
private static void printFileJava7() throws IOException {
try(FileInputStream input = new FileInputStream("d:\\hello.txt")) {
int data = input.read();
while(data != -1){
System.out.print((char) data);
data = input.read();
}
}
}Copy to clipboardErrorCopied
这就是 try-with-resource 结构的用法。FileInputStream 类型变量就在 try 关键字后面的括号中声明。而且一个 FileInputStream 类型被实例化并被赋给了这个变量。
当 try 语句块运行结束时,FileInputStream 会被自动关闭。这是因为 FileInputStream 实现了 Java 中的 java.lang.AutoCloseable 接口。所有实现了这个接口的类都可以在 try-with-resources 结构中使用。
当 try-with-resources 结构中抛出一个异常,同时 FileInputStream 被关闭时(调用了其 close 方法)也抛出一个异常,try-with-resources 结构中抛出的异常会向外传播,而 FileInputStream 被关闭时抛出的异常被抑制了。这与文章开始处利用旧风格代码的例子(在 finally 语句块中关闭资源)相反。
AutoCloseable
AutoCloseable 是 Java 的内置接口,继承这个接口并且按要求新建 close() 方法,该类就能被 try-with-resources 语法所支持。
public class AC implements AutoCloseable {
@Override
public void close() throws Exception {
System.out.println("Program has been closed pretended.");
}
//默认静态方法,在被实例化时执行
static {
System.out.println("Program running.");
}
}
public class AC2 implements AutoCloseable {
@Override
public void close() throws Exception {
System.out.println("Program 2 has been closed pretended.");
}
static {
System.out.println("Program 2 running.");
}
}Copy to clipboardErrorCopied
AC2 和 AC 在实现上是相同的。我创建两个类的原因是想让大家知道 try-with-resources 可以支持同时进行多个类的关闭。再编写一个主方法,运行测试:
public class Main {
public static void main(String[] args) {
try (AC ac = new AC();
AC2 ac2 = new AC2()) {
//这里假装执行了有用的代码
Thread.sleep(2000);
} catch (Exception e) {
}
}
}