前几天看了一篇文章(见参考文章),自己动手试了下,发现有些不一样结论,作博客记录下,本文主要研究两个问题:

包装流的close方法是否会自动关闭被包装的流?

关闭流方法是否有顺序?

包装流的close方法是否会自动关闭被包装的流?

平时我们使用输入流和输出流一般都会使用buffer包装一下,

直接看下面代码(这个代码运行正常,不会报错)

1 importjava.io.BufferedOutputStream;2 importjava.io.FileOutputStream;3 importjava.io.IOException;4

5

6 public classIOTest {7

8 public static void main(String[] args) throwsIOException {9

10 FileOutputStream fileOutputStream = new FileOutputStream("c:\\a.txt");11 BufferedOutputStream bufferedOutputStream = newBufferedOutputStream(fileOutputStream);12

13 bufferedOutputStream.write("test write something".getBytes());14 bufferedOutputStream.flush();15

16 //从包装流中关闭流

17 bufferedOutputStream.close();18 }19

20 }

下面我们来研究下这段代码的bufferedOutputStream.close();方法是否调用了fileOutputStream.close();

先看BufferedOutputStream源代码:

public class BufferedOutputStream extends FilterOutputStream { ...

1

可以看到它继承FilterOutputStream,并且没有重写close方法,

所以直接看FilterOutputStream的源代码:

1 public void close() throwsIOException {2 try{3 flush();4 } catch(IOException ignored) {5 }6 out.close();7 }

跟踪out(FilterOutputStream中):

1 protectedOutputStream out;2

3 publicFilterOutputStream(OutputStream out) {4 this.out =out;5 }

再看看BufferedOutputStream中:

1 publicBufferedOutputStream(OutputStream out) {2 this(out, 8192);3 }4

5 public BufferedOutputStream(OutputStream out, intsize) {6 super(out);7 if (size <= 0) {8 throw new IllegalArgumentException("Buffer size <= 0");9 }10 buf = new byte[size];11 }

可以看到BufferedOutputStream调用super(out);,也就是说,out.close();调用的是通过BufferedOutputStream传入的被包装的流,这里就是FileOutputStream。

我们在看看其他类似的,比如BufferedWriter的源代码:

1 public void close() throwsIOException {2 synchronized(lock) {3 if (out == null) {4 return;5 }6 try{7 flushBuffer();8 } finally{9 out.close();10 out = null;11 cb = null;12 }13 }14 }

通过观察各种流的源代码,可得结论:包装的流都会自动调用被包装的流的关闭方法,无需自己调用。

关闭流方法是否有顺序?

由上面的结论,就会产生一个问题:如果手动关闭被包装流会怎么样,这个关闭流有顺序吗?而实际上我们习惯都是两个流都关闭的。

首先我们来做一个简单的实验,基于第一个问题的代码上增加手动增加关闭流的代码,那么就有两种顺序:

1.先关闭被包装流(正常没异常抛出)

1 importjava.io.BufferedOutputStream;2 importjava.io.FileOutputStream;3 importjava.io.IOException;4

5

6 public classIOTest {7

8 public static void main(String[] args) throwsIOException {9

10 FileOutputStream fileOutputStream = new FileOutputStream("c:\\a.txt");11 BufferedOutputStream bufferedOutputStream = newBufferedOutputStream(fileOutputStream);12

13 bufferedOutputStream.write("test write something".getBytes());14 bufferedOutputStream.flush();15

16 fileOutputStream.close();//先关闭被包装流

17 bufferedOutputStream.close();18 }19

20 }

2.先关闭包装流(正常没异常抛出)

1 importjava.io.BufferedOutputStream;2 importjava.io.FileOutputStream;3 importjava.io.IOException;4

5

6 public classIOTest {7

8 public static void main(String[] args) throwsIOException {9

10 FileOutputStream fileOutputStream = new FileOutputStream("c:\\a.txt");11 BufferedOutputStream bufferedOutputStream = newBufferedOutputStream(fileOutputStream);12

13 bufferedOutputStream.write("test write something".getBytes());14 bufferedOutputStream.flush();15

16

17 bufferedOutputStream.close();//先关闭包装流

18 fileOutputStream.close();19

20 }21

22 }

上述两种写法都没有问题,我们已经知道bufferedOutputStream.close();会自动调用fileOutputStream.close();方法,那么这个方法是怎么执行的呢?我们又看看

1 FileOutputStream的源码:2

3 public void close() throwsIOException {4 synchronized(closeLock) {5 if(closed) {6 return;7 }8 closed = true;9 }10

11 ...

可以看出它采用同步锁,而且使用了关闭标记,如果已经关闭了则不会再次操作,所以多次调用不会出现问题。

如果没有看过参考文章,我可能就会断下结论,关闭流不需要考虑顺序

我们看下下面的代码(修改自参考文章):

1 importjava.io.BufferedWriter;2 importjava.io.FileOutputStream;3 importjava.io.IOException;4 importjava.io.OutputStreamWriter;5

6 public classIOTest {7

8 public static void main(String[] args) throwsIOException {9

10 FileOutputStream fos = new FileOutputStream("c:\\a.txt");11 OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF-8");12 BufferedWriter bw = newBufferedWriter(osw);13 bw.write("java IO close test");14

15 //从内带外顺序顺序会报异常

16 fos.close();17 osw.close();18 bw.close();19

20 }21

22 }

会抛出Stream closed的IO异常:

1 Exception in thread "main"java.io.IOException: Stream closed2 at sun.nio.cs.StreamEncoder.ensureOpen(StreamEncoder.java:45)3 at sun.nio.cs.StreamEncoder.write(StreamEncoder.java:118)4 at java.io.OutputStreamWriter.write(OutputStreamWriter.java:207)5 at java.io.BufferedWriter.flushBuffer(BufferedWriter.java:129)6 at java.io.BufferedWriter.close(BufferedWriter.java:264)7 at IOTest.main(IOTest.java:18)

而如果把bw.close();放在第一,其他顺序任意,即修改成下面两种:

1 bw.close();2 osw.close();3 fos.close();

1 bw.close();2 fos.close();3 osw.close();

都不会报错,这是为什么呢,我们立即看看BufferedWriter的close源码:

1 public void close() throwsIOException {2 synchronized(lock) {3 if (out == null) {4 return;5 }6 try{7 flushBuffer();8 } finally{9 out.close();10 out = null;11 cb = null;12 }13 }14 }

里面调用了flushBuffer()方法,也是抛异常中的错误方法:

1 void flushBuffer() throwsIOException {2 synchronized(lock) {3 ensureOpen();4 if (nextChar == 0)5 return;6 out.write(cb, 0, nextChar);7 nextChar = 0;8 }9 }

可以看到很大的一行

1 out.write(cb, 0, nextChar);

这行如果在流关闭后执行就会抛IO异常,

有时候我们会写成:

1 fos.close();2 fos = null;3 osw.close();4 osw = null;5 bw.close();6 bw = null;

这样也会抛异常,不过是由于flushBuffer()中ensureOpen()抛的,可从源码中看出:

1 private void ensureOpen() throwsIOException {2 if (out == null)3 throw new IOException("Stream closed");4 }5

6

7 void flushBuffer() throwsIOException {8 synchronized(lock) {9 ensureOpen();10 if (nextChar == 0)11 return;12 out.write(cb, 0, nextChar);13 nextChar = 0;14 }15 }

如何防止这种情况?

直接写下面这种形式就可以:

1 bw.close();2 bw = null;

结论:一个流上的close方法可以多次调用,理论上关闭流不需要考虑顺序,但有时候关闭方法中调用了write等方法时会抛异常。

由上述的两个结论可以得出下面的建议:

关闭流只需要关闭最外层的包装流,其他流会自动调用关闭,这样可以保证不会抛异常。如:

1 bw.close();2 //下面三个无顺序

3 osw = null;4 fos = null;5 bw = null;

注意的是,有些方法中close方法除了调用被包装流的close方法外还会把包装流置为null,方便JVM回收。bw.close()中的:

1 public void close() throwsIOException {2 synchronized(lock) {3 if (out == null) {4 return;5 }6 try{7 flushBuffer();8 } finally{9 out.close();10 out = null;11 cb = null;12 }13 }14 }

finally中就有把out置为null的代码,所以有时候不需要自己手动置为null。(个人建议还是写一下,不差多少执行时间)

java io流不关闭_Java IO流关闭问题的深入研究相关推荐

  1. java io流拒绝访问_JAVA IO流 - 张宏良的个人空间 - OSCHINA - 中文开源技术交流社区...

    IO流 一.File类的使用 java.io.File类:文件和文件目录路径的抽象表示形式,与平台无关 File能新建.删除.重命名文件和目录,但File不能访问文件内容本身.如果需要访问文件内容本身 ...

  2. java按照io流向基类_Java IO详解

    1 Java IO流的概念,分类 1.1 Java IO流的概念 java的IO是实现输入和输出的基础,可以方便的实现数据的输入和输出操作.在java中把不同的输入/输出源(键盘,文件,网络连接等)抽 ...

  3. java io使用哪些设计模式_JAVA IO中的设计模式

    在java语言 I/O库的设计中,使用了两个结构模式,即装饰模式和适配器模式. 在任何一种计算机语言中,输入/输出都是一个很重要的部分.与一般的计算机语言相比,java将输入/输出的功能和使用范畴做了 ...

  4. java流程图表示输入 输出_Java IO基础总结

    Java IO基础总结 Java中使用IO(输入输出)来读取和写入,根据数据走向可分为输入流和输出流,用户可以从输入流中中读取信息,但不能写它,相反,对输出流,只能往输入流写,而不能读它(输入流表示从 ...

  5. java管道流有哪些_Java管道流

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 1.管道流是用来在多个线程之间进行信息传递的Java流,被号称是难使用的流,被使用的频率比较低.它提供了多线程间信息传输的一种有效手段 2.管道流包括四个 ...

  6. java 两个stream合并_Java Stream 流如何进行合并操作

    1. 前言 Java Stream Api 提供了很多有用的 Api 让我们很方便将集合或者多个同类型的元素转换为流进行操作.今天我们来看看如何合并 Stream 流. 2. Stream 流的合并 ...

  7. java文件流写入文件_JAVA IO流编程 实现文件的写入、写出以及拷贝

    一.流的概念 流:数据在数据源(文件)和程序(内存)之间经历的路径. 输入流:数据从数据源(文件)到程序(内存)的路径. 输出流:数据从程序(内存)到数据源(文件)的路径. 以内存为参照,如果数据向内 ...

  8. Java io字符流读入英文_Java IO 系列教程(四)-字符输入流(2)

    本文介绍字符输入流 在前面一节中,我们向一个文件中写入了一些字符,通过图片可以看出总共是6个中文字符和一个换行,总共是20个字节,可以推算出字符编码是utf-8,每个汉子占3三个字节.本文就用字符输入 ...

  9. java io流屏幕输出_java IO流 之 输出流 OutputString()的使用

    FileOutPutStream:子类,写出数据的通道 步骤: 1.获取目标文件 2.创建通道(如果原来没有目标文件,则会自动创建一个) 3.写入数据 write() 4.释放资源 注意: (1)如果 ...

最新文章

  1. 求字符串全排列 python实现
  2. Python访问街区10个点,并俩俩绘制一条线,得到5条线,求最短的距离和?
  3. JS中confirm,alert,prompt函数
  4. blfs(systemd版本)学习笔记-配置远程访问和管理lfs系统
  5. Vue.js——vue-resource全攻略
  6. python基础语法-异常处理
  7. pcie ep 应该支持哪种interrupt_7寸国产笔记本评测,酷睿处理器+8G+256G,还支持手写笔...
  8. python:校验邮箱格式
  9. 浙江大学PTA 数据结构 习题2.2 数组循环左移 (20 分)
  10. 泰拉瑞亚Terraria for Mac(动作冒险游戏)
  11. html5钟表带齿轮项目,基于HTML5的齿轮动画特效
  12. 第7.3节 Python特色的面向对象设计:协议、多态及鸭子类型
  13. 在思科路由器上配置SSH登录
  14. C语言练习:hackerrank十五关
  15. 2022年计算机二级Web程序设计复习题及答案
  16. Delphi 技术的优缺点与应用
  17. Java实现从第三方系统单点登录到致远OA
  18. 会话管理:Cookie和Session
  19. 笔记本未指定打印机服务器,打印机出现在未指定里怎么办?可以这样解决
  20. Mysql体系构架详解——内存

热门文章

  1. php测试宽带速度慢,性能测试问题排查一例——网络带宽瓶颈
  2. 博诺杯工业机器人比赛2019_关于举办第三届“汇博-博诺杯”全国高职院校工业机器人虚拟仿真大赛的通知...
  3. 类中的三个装饰器方法
  4. C#使用HttpWebRequest和HttpWebResponse上传文件示例
  5. Nhibernate学习的第一天
  6. 常用php操作redis命令整理(五)ZSET类型
  7. iOS常识名词解释 2016/04/05
  8. cocos2d-x安装
  9. 极兔68亿收购百世快递
  10. linux是ubuntu还是centos