try-with-resources是 JDK 7 中引入的一个新的异常处理机制,它能让开发人员不用显式的释放try-catch语句块中使用的资源。,比如,我们以文件资源拷贝为示例,大家所熟悉的try-catch-finally写法如下:,我们现在将其改成使用try-with-resources编程方式,你会惊奇的发现只需要简单的几行代码就可以搞定,不用显式关闭资源,方式如下:,在 JDK7 之前,在处理必须关闭的资源时,开发人员必须要牢记在try-catch语句中使用finally执行关闭资源的方法,否则随着程序不断运行,资源泄露将会累计成重大的生产事故,如果你的程序中同时打开了多个资源,你会惊奇的发,关闭资源的代码竟然比业务代码还要多,使得代码更加难以清晰的阅读和管理。,因此在这样的背景下,try-with-resources由此诞生,它的设计初衷就是旨在减轻开发人员释放try块中使用的资源负担。,习惯了try-catch-finally写法的同学,可能会发出疑问,是不是所有涉及到资源的操作都可以用try-with-resources编程?使用这种编程方式有没有坑?如果有坑,使用的时候哪些地方应该需要注意呢?....,好吧,废话也不多说了,今天我们就一起来看看try-with-resources编程原理。,try-with-resources语句能确保每个资源在语句结束时被关闭,但是有一个前提条件,那就是这个资源必须实现了java.lang.AutoCloseable接口,才可以被执行关闭。,try-with-resources编程模式中,无需开发人员显式关闭资源的前提是,这个资源必须实现java.lang.AutoCloseable接口,并且重写close方法,否则无法在try-with-resources中进行声明变量。,下面我们可以关闭单个资源为例,代码如下:,运行结果如下:,可以很清晰的看到,close方法被调用了!,下面我们再打开反编译后的TryResourceTest.class文件代码,你会惊奇发现,编译器自动给代码加上了finally方法,并且会调用close方法,将资源关闭!,也就是说,使用try-with-resources编程,其实是编译器显式的给代码了添加finally方法,省去开发人员手动关闭资源的操作!,上面我们只介绍了关闭单个资源的场景,假如有多个资源时,try-with-resources是如何关闭的呢?,下面还是举例看结果。,运行结果如下:,从结果上可以看出,try语句中越是最后使用的资源,越是最早被关闭。,关于这一点,大家可以从反编译的代码中找到原理!,正常的情况下,try语句结束时会关闭相关的资源,假如语句内部执行时发生异常,同时我们又显式的调用了finally方法,执行的顺序又是怎样的呢?,下面继续举例看结果。,运行结果如下:,可以很清晰的看到,可以得出如下结论:,大部分情况,我们通常不会担心资源的close会发生异常,现在假设如果try里声明的资源对象,当执行close方法抛异常时,他们的执行顺序又是怎样的呢?我们又如何获取这种异常呢?,还是眼见为实,下面以举例看结果。,运行结果如下:,从运行结果我们可以很清晰的看到,对于try语句块内的异常,我们可以通过e.getMessage()获取,对于close()方法抛出的异常,其实编译器对这部分的异常进行特殊处理,将其放入到集合数组中了,因此我们需要通过e.getSuppressed()方法来获取。,具体反编译后的代码如下:,在实际的使用中,不管是使用try-with-resource编程还是使用try-catch-finally编程,一定需要了解资源的close方法内部的实现逻辑,否则还是可能会导致资源泄露。,举个例子,在 Java BIO 中采用了大量的装饰器模式。当调用装饰器的 close 方法时,本质上是调用了装饰器包装的流对象的 close 方法。比如:,在上述代码中,我们从FileInputStream中读取字节,并且写入到GZIPOutputStream中。GZIPOutputStream实际上是FileOutputStream的装饰器。,由于try-with-resource的特性,实际编译之后的代码会在后面带上finally代码块,并且在里面调用fin.close()方法和out.close()方法。,我们再来看GZIPOutputStream类的close方法。,在调用out变量的close方法之前,GZIPOutputStream还做了finish操作,该操作还会继续往FileOutputStream中写压缩信息,此时如果出现异常,则out.close()方法会被略过,而out变量实际上代表的是被装饰的FileOutputStream类,这个才是最底层的资源关闭方法。,正确的做法应该是在try-with-resource中单独声明最底层的资源,保证对应的close方法一定能够被调用。在刚才的例子中,我们需要单独声明每个FileInputStream以及FileOutputStream,改成如下方式:,编译器会自动生成fout.close()的代码,这样肯定能够保证真正的流被关闭。,在处理必须关闭的资源时,使用try-with-resources语句替代try-catch-finally语句,你会惊奇的发现,编写的代码更简洁,更清晰,同时也省去了手动显式释放资源的烦恼。,因此在实际编程过程中,推荐大家采用这种方式编写,同时要关注close方法内部的实现逻辑,避免资源泄露,服务宕机!,1、知乎 - 深入理解Java try-with-resource,2、csdn - try - with - resources详解,
© 版权声明
文章版权归作者所有,未经允许请勿转载。