我们在编写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写法,那么关闭顺序是怎么定的呢?