关于Java中流关闭以及先后顺序等问题总结

Java中关闭流一直是个让人头疼的问题,很多人要么就是忘记关闭,要么就是对关闭的顺序模糊不清,实际开发过程中,也常常因为流未关闭导致应用程序出现各种莫名奇妙的Bug。所以本篇整理出了Java中Stream流(主要指IO流,非指Java1.8中流式处理)关闭中常见的几种情况,并通过自己测试和查看源码等方式给出证明过程,下面开始。

1.各种流中存在包装关系,如何关闭

try {
     InputStream inputStream = new FileInputStream("D:\\test\\test.txt");
     InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
     BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
     System.out.println(bufferedReader.readLine());
} catch (FileNotFoundException e) {
     e.printStackTrace();
} catch (IOException e) {
     e.printStackTrace();
}finally {
    try {
        inputStream.close();
        inputStreamReader.close();
        bufferedReader.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

这段代码相信大家应该都很熟悉,java中按行读取文件中的内容。这里面用到了FileInputStream,InputStreamReader和BufferedReader这3个类,主要用了BufferedReader 的readLine方法。那么问题来了,这里FileInputStream,InputStreamReader和BufferedReader这3个类都有close方法,这3个方法是否都需要关闭,以及关闭的顺序是怎么样的呢?

我们通过查看BufferdReader的close的源码可以看到:

这里是是判断了in是否为空,如果不为空则调用in的close方法。这里的in指的就是前面代码中 BufferedReader中的构造函数传入的InputStreamReader。

所以我们可以看一下InputStreamReader的构造函数和close方法:

构造函数:

 close方法:

 可以看到这里新创建了一个StreamDecoder类,close方法其实调用的也是StreamDecoder中的close方法。所以我们继续看StreamDecoder类的close方法.

 

 我们可以看到这里其实调用的也是in的close方法(in是最前面传入的FileInputStream,ch在本文例子中没有用到,是null值)。

所以我们总结后发现,其实这里面只需要关闭最外层的BufferedReader 即可。

 那么问题来了,在这个例子中,我如果只关闭inputStrem或者inputReadStream,或者我3个类都调用了close方法会发生什么呢?

我们通过上面的源码分析如果是3个类都调用close方法的话,因为包装类有ensureOpen操作,所以最终只会调用一次,不过我们可以通过源码看到InputStream继承了Closeable接口

 

这里面注释有写到如果流已经关闭,再次调用也不会受到影响。所以就算3个方法都调用了close方法,其实等同于只调用1次close。

那么如果只关闭inputStream会如何呢?我们看实际运行的结果:

这里我们看到只关闭InputStream也是可以的,这里主要是BufferedReader和InputReader写得比较严谨,做了ensureOpen验证。其实3个类始终操作的都是同一个InputStream,所以如果读者想自己写操作InputStream的工具类,一定要记得做ensureOpen验证,并且如果工具类中有多个包装关系,要确保包装中的所有类的close方法都能实现安全关闭的效果。

 此次我贴一张网上找来的图:

在这里插入图片描述

我们发现,所有的Reader和Writer都符合上述分析。

所以,如果读者使用jdk内置的文件读写工具类,都符合上述分析的特点,只需要关闭任一包装类的流即可,如果是用第三方使用的开源库,则需要读者自己确认流是否已经关闭。

 2.关于流传输中先后关闭的问题

我们看另外一个例子:

FileInputStream in = null;
FileOutputStream out = null;
try {
    in = new FileInputStream("D:\\temp\\test.txt");
    out = new FileOutputStream("D:\\temp\\test1.txt");
    byte[] bytes = new byte[1024 * 8];
    int len = 0;
    while ((len = in.read(bytes)) != -1) {
        out.write(bytes, 0, len);
        out.flush();//刷新缓冲区
    }
} catch (IOException e) {
    e.printStackTrace();
} finally {
    try {
        if (in != null) {
            in.close();
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
    try {
        if (out != null) {
            out.close();
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

 这个例子中网上有人说需要先关闭in,再关闭out,个人理解,两者关闭的先后顺序无影响,但找不到反驳的理由。。

3.AutoCloseable 接口

当然Jdk在1.7版本的时候给出了更优雅的解决方案解决上面。我们看到前面的Closeable实现了AutoCloseable接口:

 这个AutoCloseable的用法是这样的:

try(Resource resource1 = new Resource("1");
    Resource resource2 = new Resource("2");
    Resource resource3 = new Resource("3");){
}
catch (Exception e){

}

Resource 类:

public class Resource implements Closeable {
    private String name;

    public Resource(String name){
        this.name = name;
    }
    @Override
    public void close() throws IOException{
        System.out.println(name+"已经关闭");
        throw new IOException();
    }
}

运行结果:

此处可以看到,try-catch的用法有两个特点,关闭顺序是按定义的顺序,后定义先释放的原则。另外一个特点是自动捕获了IOException异常。

通过分析,我们之前提到的内置IO工具类,都可以直接使用这个写法,如果是使用第三方或者自己写的工具类,则需要看是否满足先后顺序的特点。

总结:

1.如果是用内置的Reader和Writer,那么直接关闭最外层的Reader和Writer的close方法即可

2.流传输的过程中,只要流数据都传输完成,先关闭输出流还是先关闭输入流都无影响(找不到反例证明有问题)

3.建议使用try-catch处理流关闭,内置IO流工具类都满足可直接使用,第三方流处理工具类要查看是否符合关闭先后顺序的问题

 

  • 21
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值