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

您所在的位置:网站首页 文件流的输出是什么 关于Java中流关闭以及先后顺序等问题总结

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

2023-08-22 04:48| 来源: 网络整理| 查看: 265

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流工具类都满足可直接使用,第三方流处理工具类要查看是否符合关闭先后顺序的问题

 



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3