最近在看《Effective Java》看到第九点,try-with-resources 优先于try-finally。为了理解更加透彻,个人重新分析了一下,内容如下。
在java开发中,一些网络链接或者是文件资源都需要程序员去手动调用close方法关闭,比如InputStream、OutputStream和java.sql.Connection。如果忘关了就可能造成严重的性能后果。而关闭的方法有很多种。比如finalizer、try-catch-finally、try-with-resources等等。
finalizer机制可以关闭,但是其执行性不可预测,还有可能造成内存泄漏,所以一般不使用,虽然java9还提出了cleaner机制代替了finalizer机制,但是其执行依然不可预测,因此选择就落在了try-catch-finally和try-with-resources之间。
我们先看看try-catch-finally的在不同场景下的执行顺序。
1:代码没有异常
执行顺序:try执行完整->catch不执行->finally执行
2:代码有异常且catch进行捕获
执行顺序:try执行部分->跳转catch捕获处理->finally执行
3:代码有异常且catch不捕获:这种情况没有catch
执行顺序:try执行部分->finally执行
我们可以得出一个结论,不管有没有异常,如果有finally块,那么finally块代码一定是会被执行的。
我们来看看Effective Java的案例
static String firstLineOfFile(String path) throws IOException {
BufferedReader reader = new BufferedReader(new FileReader(path));
try {
return reader.readLine();
} finally {
reader.close();
}
}
关闭一个资源还好,但是如果再添加第二个资源,代码看起来就会一团糟了。
static void copy(String src, String desc) throws IOException {
InputStream in = new FileInputStream(src);
try {
OutputStream out = new FileOutputStream(desc);
byte[] bytes = new byte[1024];
int n;
try {
while ((n = in.read(bytes)) != -1) {
out.write(bytes, 0, n);
}
} finally {
out.close();
}
} finally {
in.close();
}
}
如果再添加几个不同资源, 需要关闭的资源不仅种类多,而且数量也很多。代码就显得很臃肿,而且非常不优雅,还容易出现内存泄露的问题。
当java7 引入try-with-resources语句时,所有这些问题一下子都全部解决了。要使用这个构造的资源,必须先实现AutoCloseable接口,其中包含了单个返回void的close方法。Java类库与第三方类库中的许多类和接口,现在都实现或扩展了AutoCloseable接口,如果编写了一个类,它代表的是必须被关闭的资源,那么这个类也应该实现AutoCloseable。
以下就是使用try-with-resources的第一个范例。其中BufferedReader已经实现了AutoCloseable
static String firstLineOfFile(String path) throws IOException {
try (BufferedReader br = new BufferedReader(new FileReader(path))){
return br.readLine();
}
}
以下就是使用ry-with-resources的第二个范例。InputStream 和 OutputStream已经实现了AutoCloseable
static void copy(String src, String dst) throws IOException {
try (InputStream inputStream = new FileInputStream(src); OutputStream outputStream = new FileOutputStream(dst)){
byte[] buf = new byte[1024];
while (inputStream.read(buf) >= 0){
outputStream.write(buf,0,inputStream.read(buf));
}
}
}
使用try-with-resources不仅使代码变得更加简洁易懂,也更容易诊断,以firstLineOfFile方法为例,如果调用readLine和不可见的close方法都抛出异常,后一个异常就会被禁止,以保留第一个异常。但是我们实际上,这些异常我们都是想它们都打印出来,可以查看堆栈轨迹的。这个时候 我们可以通过getSUppressed方法还可以访问到它们,此方法也已经添加在Java7 的Throwable中了。
经过上面例子,结论非常明显,在处理必须关闭的资源时,始终要优先考虑用try-with-resources,而不是try-finally,这样得到的代码将更加简洁,就能更轻松地正确编写代码,实践证明,这个用try-finally是不可能做到的。