首页 / JAVA / Java IO流关闭问题的深入研究
Java IO流关闭问题的深入研究
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了Java IO流关闭问题的深入研究,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含6130字,纯文字阅读大概需要9分钟。
内容图文
转自:https://blog.csdn.net/maxwell_nc/article/details/49151005
前几天看了一篇文章(见参考文章),自己动手试了下,发现有些不一样结论,作博客记录下,本文主要研究两个问题:
包装流的close方法是否会自动关闭被包装的流?
关闭流方法是否有顺序?
包装流的close方法是否会自动关闭被包装的流?
平时我们使用输入流和输出流一般都会使用buffer包装一下,
直接看下面代码(这个代码运行正常,不会报错)
1 import java.io.BufferedOutputStream; 2 import java.io.FileOutputStream; 3 import java.io.IOException; 4 5 6 public class IOTest { 7 8 public static void main(String[] args) throws IOException { 910 FileOutputStream fileOutputStream = new FileOutputStream("c:\\a.txt"); 11 BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream); 1213 bufferedOutputStream.write("test write something".getBytes()); 14bufferedOutputStream.flush(); 1516//从包装流中关闭流17bufferedOutputStream.close(); 18} 1920 }
下面我们来研究下这段代码的bufferedOutputStream.close();方法是否调用了fileOutputStream.close();
先看BufferedOutputStream源代码:
public class BufferedOutputStream extends FilterOutputStream { ...
1
可以看到它继承FilterOutputStream,并且没有重写close方法,
所以直接看FilterOutputStream的源代码:
1 public void close() throws IOException { 2try { 3flush(); 4 } catch (IOException ignored) { 5} 6out.close(); 7 }
跟踪out(FilterOutputStream中):
1 protected OutputStream out; 2 3 public FilterOutputStream(OutputStream out) { 4 this.out = out; 5 }
再看看BufferedOutputStream中:
1 public BufferedOutputStream(OutputStream out) { 2 this(out, 8192); 3} 4 5public BufferedOutputStream(OutputStream out, int size) { 6super(out); 7if (size <= 0) { 8thrownew IllegalArgumentException("Buffer size <= 0"); 9} 10 buf = newbyte[size]; 11 }
可以看到BufferedOutputStream调用super(out);,也就是说,out.close();调用的是通过BufferedOutputStream传入的被包装的流,这里就是FileOutputStream。
我们在看看其他类似的,比如BufferedWriter的源代码:
1 public void close() throws IOException { 2synchronized (lock) { 3if (out == null) { 4return; 5} 6try { 7flushBuffer(); 8 } finally { 9out.close(); 10 out = null; 11 cb = null; 12} 13} 14 }
通过观察各种流的源代码,可得结论:包装的流都会自动调用被包装的流的关闭方法,无需自己调用。
关闭流方法是否有顺序?
由上面的结论,就会产生一个问题:如果手动关闭被包装流会怎么样,这个关闭流有顺序吗?而实际上我们习惯都是两个流都关闭的。
首先我们来做一个简单的实验,基于第一个问题的代码上增加手动增加关闭流的代码,那么就有两种顺序:
1.先关闭被包装流(正常没异常抛出)
1 import java.io.BufferedOutputStream; 2 import java.io.FileOutputStream; 3 import java.io.IOException; 4 5 6 public class IOTest { 7 8 public static void main(String[] args) throws IOException { 910 FileOutputStream fileOutputStream = new FileOutputStream("c:\\a.txt"); 11 BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream); 1213 bufferedOutputStream.write("test write something".getBytes()); 14bufferedOutputStream.flush(); 1516 fileOutputStream.close();//先关闭被包装流17bufferedOutputStream.close(); 18} 1920 }
2.先关闭包装流(正常没异常抛出)
1 import java.io.BufferedOutputStream; 2 import java.io.FileOutputStream; 3 import java.io.IOException; 4 5 6 public class IOTest { 7 8 public static void main(String[] args) throws IOException { 910 FileOutputStream fileOutputStream = new FileOutputStream("c:\\a.txt"); 11 BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream); 1213 bufferedOutputStream.write("test write something".getBytes()); 14bufferedOutputStream.flush(); 151617 bufferedOutputStream.close();//先关闭包装流18fileOutputStream.close(); 1920} 2122 }
上述两种写法都没有问题,我们已经知道bufferedOutputStream.close();会自动调用fileOutputStream.close();方法,那么这个方法是怎么执行的呢?我们又看看
1 FileOutputStream的源码: 2 3 public void close() throws IOException { 4synchronized (closeLock) { 5if (closed) { 6return; 7} 8 closed = true; 9} 1011 ...
可以看出它采用同步锁,而且使用了关闭标记,如果已经关闭了则不会再次操作,所以多次调用不会出现问题。
如果没有看过参考文章,我可能就会断下结论,关闭流不需要考虑顺序
我们看下下面的代码(修改自参考文章):
1 import java.io.BufferedWriter; 2 import java.io.FileOutputStream; 3 import java.io.IOException; 4 import java.io.OutputStreamWriter; 5 6 public class IOTest { 7 8 public static void main(String[] args) throws IOException { 910 FileOutputStream fos = new FileOutputStream("c:\\a.txt"); 11 OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF-8"); 12 BufferedWriter bw = new BufferedWriter(osw); 13 bw.write("java IO close test"); 1415// 从内带外顺序顺序会报异常16fos.close(); 17osw.close(); 18bw.close(); 1920} 2122 }
会抛出Stream closed的IO异常:
1 Exception in thread "main" java.io.IOException: Stream closed 2 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() throws IOException { 2synchronized (lock) { 3if (out == null) { 4return; 5} 6try { 7flushBuffer(); 8 } finally { 9out.close(); 10 out = null; 11 cb = null; 12} 13} 14 }
里面调用了flushBuffer()方法,也是抛异常中的错误方法:
1 void flushBuffer() throws IOException { 2synchronized (lock) { 3ensureOpen(); 4if (nextChar == 0) 5return; 6 out.write(cb, 0, nextChar); 7 nextChar = 0; 8} 9 }
可以看到很大的一行
1 out.write(cb, 0, nextChar);
这行如果在流关闭后执行就会抛IO异常,
有时候我们会写成:
1 fos.close(); 2 fos = null; 3osw.close(); 4 osw = null; 5bw.close(); 6 bw = null;
这样也会抛异常,不过是由于flushBuffer()中ensureOpen()抛的,可从源码中看出:
1 private void ensureOpen() throws IOException { 2if (out == null) 3thrownew IOException("Stream closed"); 4} 5 6 7void flushBuffer() throws IOException { 8synchronized (lock) { 9ensureOpen(); 10if (nextChar == 0) 11return; 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() throws IOException { 2synchronized (lock) { 3if (out == null) { 4return; 5} 6try { 7flushBuffer(); 8 } finally { 9out.close(); 10 out = null; 11 cb = null; 12} 13} 14 }
finally中就有把out置为null的代码,所以有时候不需要自己手动置为null。(个人建议还是写一下,不差多少执行时间)
原文:https://www.cnblogs.com/fnlingnzb-learner/p/10132540.html
内容总结
以上是互联网集市为您收集整理的Java IO流关闭问题的深入研究全部内容,希望文章能够帮你解决Java IO流关闭问题的深入研究所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。