关注上方蓝字关注我们

引入

Java类库中有许多资源需要通过close方法进行关闭。

比如 InputStream、OutputStream,数据库连接对象 Connection,MyBatis中的 SqlSession 会话等。作为开发人员经常会忽略掉资源的关闭方法,导致内存泄漏。

根据经验,try-finally语句是确保资源会被关闭的最佳方法,就算异常或者返回也一样。try-catch-finally 一般是这样来用的

static String firstLineOfFile(String path) throws IOException {  BufferedReader br = new BufferedReader(new FileReader(path));try {return br.readLine();  }finally {    br.close();  }}

这样看起来代码还是比较整洁,但是当我们添加第二个需要关闭的资源的时候,就像下面这样

static void copy(String src,String dst) throws Exception{        InputStream is = new FileInputStream(src);try {

    OutputStream os = new FileOutputStream(dst);try {byte[] buf = new byte[100];int n;while ((n = is.read()) >= 0){        os.write(buf,n,0);      }    }finally {      os.close();    }  }finally {    is.close();  }}

这样感觉这个方法已经变得臃肿起来了。

而且这种写法也存在诸多问题,比如:就算是 try - finally 能够正确关闭资源,但是它不能阻止异常的抛出,因为 try 和 finally 块中都可能有异常的发生。

比如说你正在读取的时候硬盘损坏,这个时候你就无法读取文件和关闭资源了,此时会抛出两个异常。但是在这种情况下,第二个异常会抹掉第一个异常。在异常堆栈中也无法找到第一个异常的记录,怎么办,难道像这样来捕捉异常么?

static void tryThrowException(String path) throws Exception {

  BufferedReader br = new BufferedReader(new FileReader(path));try {    String s = br.readLine();    System.out.println("s = " + s);

  }catch (Exception e){    e.printStackTrace();  }finally {try {      br.close();    }catch (Exception e){      e.printStackTrace();    }finally {      br.close();    }  }}

这种写法,虽然能解决异常抛出的问题,但是各种 try-cath-finally 的嵌套会让代码变得非常臃肿。

改变

Java7 中引入了try-with-resources 语句时,所有这些问题都能得到解决。要使用try-with-resources 语句,首先要实现 AutoCloseable 接口,此接口包含了单个返回的 close 方法。Java类库与三方类库中的许多类和接口,现在都实现或者扩展了 AutoCloseable 接口。如果编写了一个类,它代表的是必须关闭的资源,那么这个类应该实现 AutoCloseable 接口。

java引入了 try-with-resources 声明,将 try-catch-finally 简化为 try-catch,这其实是一种语法糖,在编译时会进行转化为 try-catch-finally 语句。

下面是使用 try-with-resources 的第一个范例

/**     * 使用try-with-resources 改写示例一     * @param path     * @return     * @throws IOException     */static String firstLineOfFileAutoClose(String path) throws IOException {

try(BufferedReader br = new BufferedReader(new FileReader(path))){return br.readLine();  }}

使用 try-with-resources 改写程序的第二个示例

static void copyAutoClose(String src,String dst) throws IOException{

try(InputStream in = new FileInputStream(src);      OutputStream os = new FileOutputStream(dst)){byte[] buf = new byte[1000];int n;while ((n = in.read(buf)) >= 0){      os.write(buf,0,n);    }  }}

使用 try-with-resources 不仅使代码变得通俗易懂,也更容易诊断。以firstLineOfFileAutoClose方法为例,如果调用 readLine() 和 close 方法都抛出异常,后一个异常就会被禁止,以保留第一个异常。

理解

异常处理有两种情况:

  1. try 块没有发生异常时,直接调用finally块,如果 close 发生异常,直接进行处理。

  2. try 块发生异常,catch 块捕捉,进行第一处异常处理,然后调用 finally 块,如果 close 发生异常,就进行第二处异常处理。

但是在 try-with-resources 结构中,异常处理也有两种情况(注意,不论 try 中是否有异常,都会首先自动执行 close 方法,然后才判断是否进入 catch 块,建议阅读后面的反编译代码):

  1. try 块没有发生异常时,自动调用 close 方法,如果发生异常,catch 块捕捉并处理异常。

  2. try 块发生异常,然后自动调用 close 方法,如果 close 也发生异常,catch 块只会捕捉 try 块抛出的异常,close 方法的异常会在catch 中被压制,但是你可以在catch块中,用 Throwable.getSuppressed 方法来获取到压制异常的数组。

下面是一个示例

public class TryWithResources {

public static void testTryWithResources(){try (MyAutoCloseA a = new MyAutoCloseA();             MyAutoCloseB b = new MyAutoCloseB()) {            a.test();            b.test();        } catch (Exception e) {            System.out.println("Main: exception");            System.out.println(e.getMessage());            Throwable[] suppressed = e.getSuppressed();for (int i = 0; i < suppressed.length; i++)                System.out.println(suppressed[i].getMessage());        }    }

public static void main(String[] args) {        testTryWithResources();    }}

class MyAutoCloseA implements AutoCloseable {

public void test() throws IOException {        System.out.println("MyAutoCloseA: test() ");throw new IOException("MyAutoCloseA: test() IOException");    }

@Overridepublic void close() throws Exception {        System.out.println("MyAutoCloseA: on close()");throw new ClassNotFoundException("MyAutoCloseA: close() ClassNotFoundException");    }}

class MyAutoCloseB implements AutoCloseable {

public void test() throws IOException {        System.out.println("MyAutoCloseB: test()");throw new IOException("MyAutoCloseB: test() IOException");    }

@Overridepublic void close() throws Exception {        System.out.println("MyAutoCloseB: on close()");throw new ClassNotFoundException("MyAutoCloseB: close() ClassNotFoundException");    }}

输出结果是这样的:

MyAutoCloseA: test() MyAutoCloseB: on close() MyAutoCloseA: on close() Main: exception MyAutoCloseA: test() IOException MyAutoCloseB: close() ClassNotFoundException MyAutoCloseA: close() ClassNotFoundException

你能猜到这个输出结果吗?

如果有疑问的话,那么先来看一下上面这段代码反编译之后的结果吧

反编译后的执行过程

public static void startTest() {try {    MyAutoCloseA a = new MyAutoCloseA();    Throwable var33 = null;

try {      MyAutoCloseB b = new MyAutoCloseB();      Throwable var3 = null;

try { // 我们定义的 try 块        a.test();        b.test();      } catch (Throwable var28) { // try 块中抛出的异常        var3 = var28;throw var28;      } finally {if (b != null) {// 如果 try 块中抛出异常,就将 close 中的异常(如果有)附加为压制异常if (var3 != null) {try {              b.close();            } catch (Throwable var27) {              var3.addSuppressed(var27);            }          } else { // 如果 try 块没有抛出异常,就直接关闭,可能会抛出关闭异常            b.close();          }        }

      }    } catch (Throwable var30) {      var33 = var30;throw var30;    } finally {if (a != null) {if (var33 != null) {try {            a.close();          } catch (Throwable var26) {            var33.addSuppressed(var26);          }        } else {          a.close();        }      }    }// 所有的异常在这里交给 catch 块处理  } catch (Exception var32) { // 我们定义的 catch 块    System.out.println("Main: exception");    System.out.println(var32.getMessage());    Throwable[] suppressed = var32.getSuppressed();

for(int i = 0; i < suppressed.length; ++i) {      System.out.println(suppressed[i].getMessage());    }  }}

try 块中的关闭顺序是从后向前进行关闭,也就是说,在创建完成 a 和 b 对象后,对 a 调用test() 方法,会先输出A 的信息,然后抛出异常进行关闭,自动调用 close() 方法,执行关闭的顺序是从后向前执行,所以会先关闭 b 的对象,会自动调用 b 的close 方法,然后会调用 a 的 close 方法。

因为对象 a 在执行test 方法的时候抛出了异常,所以对异常进行捕获,输出 Main: exception,然后获取 A 的异常信息,因为close 方法抛出的异常在 catch 中被压制,可以通过 Throwable.getSuppressed 进行输出,因为 B 先调用 close() 方法出现的异常,所以先输出了 MyAutoCloseB: close() ClassNotFoundException ,而最后输出的是 MyAutoCloseA: close() ClassNotFoundException

总结

结论很明显,在处理资源关闭的时候,始终要优先考虑使用 try-with-resources ,而不是 try-finally。这样得到的代码更加简洁、清晰,产生的异常也更有价值。

你点的每个好看,我都认真当成了喜欢

io流不关闭会怎么样_关闭IO流,你确定不试试trywithresources?相关推荐

  1. io流不关闭会怎么样_幸福树一个月不浇水会怎么样

    许多盆友喜欢养幸福树,而且多将其作为盆栽来养,或将其摆放在室内,或将其摆放在阳台或庭院里,往往装点环境的效果都非常不错.尤其是它的枝叶众多,叶色苍翠且具有光泽感,看起来十分醒目,尤其是在夏天里非常养眼 ...

  2. io操作是指什么_各种IO模型,一篇打尽

    一.阻塞/非阻塞-同步非同步 同步/异步 同步请求:A调用B,B的处理是同步的,在处理完之前他不会通知A,只有处理完之后才会明确的通知A; 异步请求:A调用B,B的处理是异步的,B在接到请求后先告诉A ...

  3. 站长便民小工具引流网站源码_站长引流工具箱

    介绍: 站长便民小工具引流网站源码,站长引流工具箱,内附各种实用小工具,接口是别人的. 可以使用 网盘下载地址: http://kekewl.cc/atT7j70pUXI 图片:

  4. java流式布局换行_自动换行的流式布局

    1.[代码][Java]代码 package com.robert; import android.content.Context; import android.util.AttributeSet; ...

  5. 关闭流时,抛出异常:java.io.IOException: Stream Closed

    首先,这个异常大部分原因是因为关闭流的顺序导致的,下面看一下官方API的描述 void close()throws IOExceptionCloses this stream and releases ...

  6. java关闭io流_Java IO流关闭问题的深入研究

    前言 前几天看了一篇文章(见参考文章),自己动手试了下,发现有些不一样结论,作博客记录下,本文主要研究两个问题: 1.包装流的close方法是否会自动关闭被包装的流? 答:会. 2.关闭流方法是否有顺 ...

  7. io读取文件无地址_流的基本概念和IO流入门

    对于任何程序设计语言而言,输入输出(Input/Output)系统都是非常核心的功能.程序运行需要数据,数据的获取往往需要跟外部系统进行通信,外部系统可能是文件.数据库.其他程序.网络.IO设备等等. ...

  8. 第十三章、IO流_File类与递归/基本流/增强流/属性集与打印流__黑马Java第57期个人学习笔记_个人笔记

    第一节.File类与递归 一.File类 (一)概念: 1.File类是文件和目录路径名的抽象表示,主要用于文件和目录的创建.查找和删除等操作.(也重写了toString为getPath) 2.绝对路 ...

  9. java中io流实现哪个接口_第55节:Java当中的IO流-时间api(下)-上

    标题图 Java当中的IO流(下)-上日期和时间日期类:java.util.Date 系统时间:long time = System.currentTimeMillis();public class  ...

  10. Java中使用try-with-resource优雅的关闭io流

    前言 最近看到一篇关于串流的关闭方法,通过编译器自动生成关闭串流源码,行之有效. 转载自博主: Java劝退师. https://blog.csdn.net/qq_41389354/article/d ...

最新文章

  1. PCL:点云中的超体素数据
  2. jq父级绑定事件的意义_jq——事件
  3. SAP ME12 修改采购信息记录,系统提示:Condition type P000 does not allow supplementary conditions
  4. OSPF路由配置实例
  5. 微软代号为“Volta”的编程工具集预览
  6. 赛码浪潮笔试题库软件实施岗位_2020年浪潮软件类笔试题
  7. 微软新闻:英雄由此诞生
  8. 6、Power Query-SQL与PQ技术的强强联合
  9. Acess link
  10. Spring——bean生命周期
  11. java 嵌套事务_Java事务以及嵌套事务
  12. 美国伯克利大学计算机研究生学几年,美国加州大学伯克利分校计算机CS研究生申请条件一览...
  13. 解决Mac网络连接问题的一些方法
  14. 广州坐标系转换大地2000_如何将百度坐标转换为国家2000坐标系?
  15. unity实现简单的地图编辑器,实现跑酷地图编辑器 2d地图编辑器,导出地图json数据,导入地图json数据
  16. oracle10g dblink优化,dblink如果很慢可以用这种方式优化
  17. 茶叶分类--六大茶类
  18. 我的macbook应用清单
  19. Java 使用JavaMail通过Gmail发送电子邮件
  20. CoreAudioApi-音频端点设备-检测耳机插拔

热门文章

  1. tinycc update VERSION to 0.9.27
  2. 用excel来做项目管理?
  3. 【读书笔记】 多线程程序常见bug
  4. 小小方法,问题锦集。
  5. Linux高性能集群搭建(2)---NFS共享文件系统安装配置
  6. 20200517每日一句
  7. 20190916每日一句
  8. 190707每日一句,一堂重要的人生之课Let it go, 穷则变变则通
  9. holotoolkit 客户端与服务器架构的建立
  10. Atitit 项目wechat微信截屏生成vcf通讯录384 个 384个人 42个节拍,平均每个8个人 技术点 im图像裁剪, ocr Tesseract Vcf格式 /wechatTel