我们在编写IO代码的时候,有的时候真的是对对java IO那种模板化的代码感到厌倦,而且写出来的代码,很臃肿丑陋。像下面这样的代码:
public void readFile(String filePath) {
FileInputStream fis = null;
InputStreamReader inReader = null;
BufferedReader br = null;
try {
fis = new FileInputStream(filePath);
inReader = new InputStreamReader(fis);
br = new BufferedReader(inReader);
for (String line; (line = br.readLine()) != null; ) {
//TODO
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (null != fis) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (null != inReader) {
try {
inReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (null != br) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
真的是看花眼啊,尤其是那个finally块,太长了。而且有的时候,忘记关闭资源,导致资源被耗尽,程序出现异常,还得各种定位问题。
java1.7带来了一个新特性,即TWR(Try-With-Resource)语法,我们可以不用去管那finally块了,只需将上面的代码改写成这样即可。
public void readFile(String filePath) {
try (FileInputStream fis = new FileInputStream(filePath);
InputStreamReader inReader = new InputStreamReader(fis);
BufferedReader br = new BufferedReader(inReader);) {
for (String line; (line = br.readLine()) != null; ) {
//TODO
}
} catch (IOException e) {
e.printStackTrace();
}
}
即,在try()括号内初始化IO变量。这样来看,代码一下子精简不少。我们再也不用关注那臃肿的finally块了。
那么java1.7是怎么做到的呢?我们将上面的代码,放到TWR.java源文件中,并import相应的类,然后在JDK1.7.0_80用javac编译一下,得到TWR.class文件。如图:
借助于IDEA,我们反编译了这份字节码文件,看看javac都为我们做了什么。
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
public class TWR {
public TWR() {
}
public void readFile(String var1) {
try {
FileInputStream var2 = new FileInputStream(var1);
Throwable var3 = null;
try {
InputStreamReader var4 = new InputStreamReader(var2);
Throwable var5 = null;
try {
BufferedReader var6 = new BufferedReader(var4);
Throwable var7 = null;
try {
while(true) {
if (var6.readLine() != null) {
continue;
}
}
} catch (Throwable var54) {
var7 = var54;
throw var54;
} finally {
if (var6 != null) {
if (var7 != null) {
try {
var6.close();
} catch (Throwable var53) {
var7.addSuppressed(var53);
}
} else {
var6.close();
}
}
}
} catch (Throwable var56) {
var5 = var56;
throw var56;
} finally {
if (var4 != null) {
if (var5 != null) {
try {
var4.close();
} catch (Throwable var52) {
var5.addSuppressed(var52);
}
} else {
var4.close();
}
}
}
} catch (Throwable var58) {
var3 = var58;
throw var58;
} finally {
if (var2 != null) {
if (var3 != null) {
try {
var2.close();
} catch (Throwable var51) {
var3.addSuppressed(var51);
}
} else {
var2.close();
}
}
}
} catch (IOException var60) {
var60.printStackTrace();
}
}
}
通过这份反编译代码,我们知道了,原来是javac在编译的时候,悄悄地为我们加上了finally块,而且它的写法更加安全。
在java类库中,也有很多TWR的身影,像java.nio.file.Files类的readAllLines()方法:
代码截取自JDK_1.9.0.1的源码:
public static List<String> readAllLines(Path path, Charset cs) throws IOException {
try (BufferedReader reader = newBufferedReader(path, cs)) {
List<String> result = new ArrayList<>();
for (;;) {
String line = reader.readLine();
if (line == null)
break;
result.add(line);
}
return result;
}
}
【总结】:TWR写法告诉了编译器javac,自动的为我们增加了资源关闭代码。
【问题】:如果不采用TWR写法,那么关闭顺序是怎么定的呢?