这是我参与8月更文挑战的第21天,活动详情查看:8月更文挑战
前言
手动关闭的资源有很多,文件流,输入输出流,数据库连接,文件流如果不关闭,结果就是文件打开就是损坏状态。数据库连接不关闭,结果就是连接耗尽,拒绝访问。但是很多人都会忘记关闭资源,随之而来的是预料之际严重的性能影响。这些资源中大部分都使用了 finalizer 作为兜底方法来关闭资源,但 我们在创建和销毁对象-避免使用 Finalizer 和 Cleaner 中已经介绍了finalizer 并非一个好选择。
过去的最佳方式
在过去,try-finally 语句是保证资源被正确关闭的最佳方式 ,即使在遇到异常或返回时依然如此:
// try-finally —— 不再是关闭资源的最佳方式!
static String firstLineOfFile(String path) throws IOException {
BufferedReader br = new BufferedReader(new FileReader(path));
try {
return br.readLine();
} finally {
br.close();
}
}
这看起来可能还不错,但是当资源变多的时候,就会try-finally里面套try-finally了finally里面都是close
// 当使用多个资源时,try-finally 就变得臃肿不堪!
static void copy(String src, String dst) throws IOException {
InputStream in = new FileInputStream(src);
try {
OutputStream out = new FileOutputStream(dst);
try {
byte[] buf = new byte[BUFFER_SIZE];
int n;
while ((n = in.read(buf)) >= 0)
out.write(buf, 0, n);
} finally {
out.close();
}
}
finally {
in.close();
}
}
互相嵌套就会导致有可能error发生时,close的错误将真正的报错掩盖掉,虽然可以在close那再加try-catch,但是真的是太繁琐了。
解决方案
Java 7 中的 try-with-resources 语句后,所有的这些问题一并得到了解决。要使用try-with-resources,资源必须实现 AutoCloseable 接口,该接口由一个没有返回值(void-returning)的 close 方法组成。
比如InputStream实现了Closeable,而Closeable则继承了AutoCloseable。到现在,基本上大部分的类都是用try-with-resources 来关闭必须要关闭的资源的,你也应该这样做!
下面是第一个示例使用 try-with-resources 语句块之后的样子:
// try-with-resources —— 关闭资源的最佳方式!
static String firstLineOfFile(String path) throws IOException {
try (BufferedReader br = new BufferedReader(new FileReader(path))) {
return br.readLine();
}
}
如果是多个资源的话中间可以用分号进行分割
try (InputStream in = new FileInputStream(src);
OutputStream out = new FileOutputStream(dst))
try-with-resources实质上是语法糖
虽然是个语法糖,但是try-with-resources用起来比try-finally要好用多了。代码更短更易阅读。
try (Conn conn = new Conn()) {
conn.send();
}
catch (Exception e) {
e.printStackTrace();
}
上面是一个使用了try-with-resources的例子,对应的Conn对象的代码
可以看到代码中实现了AutoCloseable,覆盖了接口的close方法,而我们去看对应生成的Class文件,则可以发现,实际上本质上还是try-finally,只不过通过这个语法糖帮我们节省了代码的编写的过程
编译器帮我们调用了close方法。
优先使用 try-with-resources
要使用必须关闭的资源,就是用 try-with-resources ,使用它写成的代码更短更清晰,产生的异常信息也更有用。