众所周知,所有被打开的系统资源,比如流,文件或者插座连接等,都需要被开发者手动关闭,否则随着程序的不断运行,资源泄露将会累积成重大的生产事故。
在Java的江湖中,存在着一种名为最后的功夫,它可以保证当你习武走火入魔之时,还可以做一些自救的操作。在远古时代,处理资源关闭的代码通常写在最后块中。然而,如果你同时打开了多个资源,那么将会出现噩梦般的场景:
公开课演示{ 公共静态void main (String [] args) { BufferedInputStream本=零; 布特BufferedOutputStream=零; 尝试{ 本=new BufferedInputStream(新FileInputStream(新文件(“用法”))); 布特=new BufferedOutputStream(新FileOutputStream(新文件(“out.txt”))); int b; 在((b=bin.read ()) !=1) { bout.write (b); } } 抓住(IOException e) { e.printStackTrace (); } 最后{ 如果(本!=null) { 尝试{ bin.close (); } 抓住(IOException e) { 把e; } 最后{ 如果(布特!=null) { 尝试{ bout.close (); } 抓住(IOException e) { 把e; } } } } } } }
哦,我的上帝! ! !关闭资源的代码竟然比业务代码还要多! ! !这是因为,我们不仅需要关闭<代码> BufferedInputStream> 代码,还需要保证如果关闭<代码> BufferedInputStream> 代码时出现了异常,<代码> BufferedOutputStream> 代码也要能被正确地关闭,所以我们不得不借助最后中嵌套最后大法。可以想到,打开的资源越多,最后中嵌套的将会越深! ! !
Java 1.7中新增的try-with-resource语法糖来打开资源,而无需码农们自己书写资源来关闭代码。再也不用担心我把手写断掉了!我们用try-with-resource来改写刚才的例子:
公开课TryWithResource { 公共静态void main (String [] args) { 试(BufferedInputStream本=new BufferedInputStream(新FileInputStream(新文件(“用法”))); 布特BufferedOutputStream=new BufferedOutputStream(新FileOutputStream(新文件(“out.txt”)))) { int b; 在((b=bin.read ()) !=1) { bout.write (b); } } 抓住(IOException e) { e.printStackTrace (); } } }
为了能够配合try-with-resource,资源必须实现<代码> AutoClosable 代码>接口。该接口的实现类需要重写<代码> 代码>密切方法:
公共类连接实现AutoCloseable { 公共空间sendData () { System.out.println(“正在发送数据”); } @Override 公共空间close()抛出异常{ System.out.println(“正在关闭连接”); } } >之前调用类:
公开课TryWithResource { 公共静态void main (String [] args) { 试(连接康涅狄格州=新连接()){ conn.sendData (); } 捕获(异常e) { e.printStackTrace (); } } } >之前运行后输出结果:
正在发送数据
引用>
正在关闭连接
那么这个是怎么做到的呢?我相信聪明的你们一定已经猜到了,其实,这一切都是编译器大神搞的鬼。我们反编译刚才例子的类文件:
包com.codersm.trywithresource; 公开课TryWithResource { 公共TryWithResource () { } 公共静态void main (String [] args) { 尝试{ 连接康涅狄格州=new连接(); Throwable var2=零; 尝试{ conn.sendData (); }捕捉(Throwable var12) { var2=var12; 把var12; 最后}{ 如果(康涅狄格州!=null) { 如果(var2 !=null) { 尝试{ conn.close (); }捕捉(Throwable var11) { var2.addSuppressed (var11); } 其他}{ conn.close (); } } } }捕捉(异常var14) { var14.printStackTrace (); } } } >之前看到没,在第15 ~ 27行,编译器自动帮我们生成了最后块,并且在里面调用了资源附近的方法,所以例子中附近的方法会在运行的时候被执行。
细心的你们肯定又发现了,刚才反编译的代码(第21行)比远古时代写的代码多了一个addSuppressed。为了了解这段代码的用意,我们稍微修改一下刚才的例子:我们将刚才的代码改回远古时代手动关闭异常的方式,并且在sendData和密切的方法中抛出异常:
深入理解Java基础之try-with-resource语法糖