前言

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

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

答:会。

2、关闭流方法是否有顺序?

答:无。

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

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

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

importjava.io.BufferedOutputStream;importjava.io.FileOutputStream;importjava.io.IOException;public classIOTest {public static void main(String[] args) throwsIOException {

FileOutputStream fileOutputStream= new FileOutputStream("c:\\a.txt");

BufferedOutputStream bufferedOutputStream= newBufferedOutputStream(fileOutputStream);

bufferedOutputStream.write("test write something".getBytes());

bufferedOutputStream.flush();//从包装流中关闭流

bufferedOutputStream.close();

}

}

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

先看BufferedOutputStream源代码:

public class BufferedOutputStream extends FilterOutputStream { ...

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

所以直接看FilterOutputStream的源代码:

public void close() throwsIOException {try{

flush();

}catch(IOException ignored) {

}

out.close();

}

跟踪out(FilterOutputStream中):

protectedOutputStream out;publicFilterOutputStream(OutputStream out) {this.out =out;

}

再看看BufferedOutputStream中:

publicBufferedOutputStream(OutputStream out) {this(out, 8192);

}public BufferedOutputStream(OutputStream out, intsize) {super(out);if (size <= 0) {throw new IllegalArgumentException("Buffer size <= 0");

}

buf= new byte[size];

}

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

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

public void close() throwsIOException {synchronized(lock) {if (out == null) {return;

}try{

flushBuffer();

}finally{

out.close();

out= null;

cb= null;

}

}

}

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

关闭流方法是否有顺序?

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

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

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

importjava.io.BufferedOutputStream;importjava.io.FileOutputStream;importjava.io.IOException;public classIOTest {public static void main(String[] args) throwsIOException {

FileOutputStream fileOutputStream= new FileOutputStream("c:\\a.txt");

BufferedOutputStream bufferedOutputStream= newBufferedOutputStream(fileOutputStream);

bufferedOutputStream.write("test write something".getBytes());

bufferedOutputStream.flush();

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

bufferedOutputStream.close();

}

}

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

importjava.io.BufferedOutputStream;importjava.io.FileOutputStream;importjava.io.IOException;public classIOTest {public static void main(String[] args) throwsIOException {

FileOutputStream fileOutputStream= new FileOutputStream("c:\\a.txt");

BufferedOutputStream bufferedOutputStream= newBufferedOutputStream(fileOutputStream);

bufferedOutputStream.write("test write something".getBytes());

bufferedOutputStream.flush();

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

fileOutputStream.close();

}

}

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

public void close() throwsIOException {synchronized(closeLock) {if(closed) {return;

}

closed= true;

}

...

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

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

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

importjava.io.BufferedWriter;importjava.io.FileOutputStream;importjava.io.IOException;importjava.io.OutputStreamWriter;public classIOTest {public static void main(String[] args) throwsIOException {

FileOutputStream fos= new FileOutputStream("c:\\a.txt");

OutputStreamWriter osw= new OutputStreamWriter(fos, "UTF-8");

BufferedWriter bw= newBufferedWriter(osw);

bw.write("java IO close test");//从内带外顺序顺序会报异常

fos.close();

osw.close();

bw.close();

}

}

会抛出Stream closed的IO异常:

Exception in thread "main"java.io.IOException: Stream closed

at sun.nio.cs.StreamEncoder.ensureOpen(StreamEncoder.java:45)

at sun.nio.cs.StreamEncoder.write(StreamEncoder.java:118)

at java.io.OutputStreamWriter.write(OutputStreamWriter.java:207)

at java.io.BufferedWriter.flushBuffer(BufferedWriter.java:129)

at java.io.BufferedWriter.close(BufferedWriter.java:264)

at IOTest.main(IOTest.java:18)

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

bw.close();

osw.close();

fos.close();

bw.close();

fos.close();

osw.close();

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

public void close() throwsIOException {synchronized(lock) {if (out == null) {return;

}try{

flushBuffer();

}finally{

out.close();

out= null;

cb= null;

}

}

}

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

void flushBuffer() throwsIOException {synchronized(lock) {

ensureOpen();if (nextChar == 0)return;

out.write(cb,0, nextChar);

nextChar= 0;

}

}

可以看到很大的一行

out.write(cb, 0, nextChar);

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

有时候我们会写成:

fos.close();

fos= null;

osw.close();

osw= null;

bw.close();

bw= null;

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

private void ensureOpen() throwsIOException {if (out == null)throw new IOException("Stream closed");

}void flushBuffer() throwsIOException {synchronized(lock) {

ensureOpen();if (nextChar == 0)return;

out.write(cb,0, nextChar);

nextChar= 0;

}

}

如何防止这种情况?

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

bw.close();

bw= null;

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

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

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

bw.close();

//下面三个无顺序

osw = null;

fos= null;

bw= null;

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

public void close() throwsIOException {synchronized(lock) {if (out == null) {return;

}try{

flushBuffer();

}finally{

out.close();

out= null;

cb= null;

}

}

}

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

原文链接:https://blog.csdn.net/maxwell_nc/article/details/49151005

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

  1. java io字符流_Java IO流字符流简介及基本使用

    Java IO流字符流简介及常用字符流的基本使用 字符流分为输入字符流(Writer)和输出字符流(Reader),这两种字符流及其子类字符流都有自己专门的功能.在编码中我们常用的输出字符流有File ...

  2. java io流过滤流_JAVA io流 文件流 字节流 字符流 过滤流 缓冲流

    一.概念 1.1.按流向分类: 输入流: 程序可以从中读取数据的流. 输出流: 程序能向其中写入数据的流. 1.2.按数据传输单位分类: 字节流:以字节(8位二进制)为单位进行处理.主要用于读写诸如图 ...

  3. JAVA mac系统io文件流_Java IO流基础1--IO的分类体系与文件流

    什么是IO流 Java中的IO 了解什么是IO流之前,要先知道什么是IO.IO,就是in和out(即输入和输出),指应用程序和外部设备之间的数据传递,常见的外部设备包括文件.管道.网络连接等. 流的概 ...

  4. java io流填空题,java面试题选择题_java,Io流面试题和选择题

    Java对象的序列化指将一个java对象写入OI流中,与此对应的是,对象的反序列化则从IO流中恢复该java对象.下面就由小编为大家介绍一下java Io流面试题和选择题的文章,欢迎阅读. java ...

  5. java 文件转换成流_java -IO流_转换流

    转换流 在学习字符流(FileReader.FileWriter)的时候,其中说如果需要指定编码和缓冲区大小时,可以在字节流的基础上,构造一个InputStreamReader或者OutputStre ...

  6. 字节流转化为文件流_JAVA IO分析一:File类、字节流、字符流、字节字符转换流...

    因为工作事宜,又有一段时间没有写博客了,趁着今天不是很忙开始IO之路:IO往往是我们忽略但是却又非常重要的部分,在这个讲究人机交互体验的年代,IO问题渐渐成了核心问题. 一.File类 在讲解File ...

  7. java io 输出流_Java IO 输入和输出流

    数据流是指一组有顺序的,有起点和终点的字节集合. 最初的版本中,java.io 包中的流只有普通的字节流,即以 byte 为基本处理单位的流.字节流用来读写 8 位的数据,由于不会对数据做任何转换,因 ...

  8. Java 重定向 无法写入_java IO 文件读入,写入,重定向

    Java代码 packagestar20110526; importjava.io.BufferedInputStream; importjava.io.BufferedOutputStream; i ...

  9. java序列化流_java 序列化流与反序列化流

    一 对象序列化流ObjectOutputStream ObjectOutputStream 将Java对象的基本数据类型和图形写入OutputStream.可以使用ObjectInputStream ...

最新文章

  1. Linux命令技巧之30个必会的命令技巧
  2. docker 容器互访三种方式
  3. 哪吒票房逼近40亿,用python爬取哪吒短评分析
  4. 【T_SQL】 基础 事务
  5. 客观指标VS主观质量:拨开遮挡高清视频技术的迷雾
  6. 什么是 CMS - Content Management System
  7. JAVA中pin什么意思_java语言中的多态概述
  8. javascript中children和childNodes的区别
  9. 前端学习(2041)vue之电商管理系统电商系统之只是在发布阶段生效
  10. c语言入门数据类型详解,C语言的基本数据类型入门教程
  11. 删除高频分量matlab,关于FFT之后怎么提出该频率分量
  12. shiro配置文件shiro.ini简介说明
  13. 阿里icon使用fontclass
  14. Sqli-Labs Less1-16关详细讲解
  15. 所有的 Python 库都整理
  16. 第八届蓝桥杯C/C++程序设计本科B组决赛 ——瓷砖样式(填空题)【DP?我的暴力排列搜索】...
  17. Matlab在win10运行不出图片,win10系统网页图片加载不出来的六种原因及解决方法...
  18. 小米 Redmi airDots 2耳机一只一直在亮白光怎么办
  19. 世界观和方法论——青山行客
  20. Win10电脑更新提示错误0xc1900403怎么解决?

热门文章

  1. 用jiebaR分析比特币的文章
  2. TPAMI 2020 | 无监督多类域适应:理论,算法与实践
  3. 挑战弱监督学习的三大热门问题,AutoWSL2019挑战赛正式开赛
  4. 第四期 | 带学斯坦福CS224n自然语言处理课+带打全球Kaggle比赛(文末重金招募老师!)...
  5. 【百度飞浆】目标检测综述
  6. 剑指Offer_编程题 不用加减乘除做加法
  7. python pandas dataframe 转json_python将相类不同key的json对象转化为pandas的dataframe(上篇)...
  8. 模拟FCFS调度算法(先来先服务)没错,是篇好文章!
  9. 十大经典排序算法之希尔排序及其优化
  10. 报错Submitted credentials for token did not match the expected credentials