前几天看了一篇文章,本人入手试了下,发现有些不一样论断,作博客记录下,本文次要钻研两个问题:
- 包装流的close办法是否会主动敞开被包装的流?
- 敞开流办法是否有程序?
包装流的close办法是否会主动敞开被包装的流?
平时咱们应用输出流和输入流个别都会应用buffer包装一下,间接看上面代码(这个代码运行失常,不会报错)
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class IOTest {
public static void main(String[] args) throws IOException {
FileOutputStream fileOutputStream = new FileOutputStream("c:\\a.txt");
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(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() throws IOException {
try {
flush();
} catch (IOException ignored) {
}
out.close();
}
跟踪out(FilterOutputStream
中):
protected OutputStream out;
public FilterOutputStream(OutputStream out) {
this.out = out;
}
再看看BufferedOutputStream
中:
public BufferedOutputStream(OutputStream out) {
this(out, 8192);
}
public BufferedOutputStream(OutputStream out, int size) {
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() throws IOException {
synchronized (lock) {
if (out == null) {
return;
}
try {
flushBuffer();
} finally {
out.close();
out = null;
cb = null;
}
}
}
通过观察各种流的源代码,可得论断:包装的流都会主动调用被包装的流的敞开办法,无需本人调用。
敞开流办法是否有程序?
由下面的论断,就会产生一个问题:如果手动敞开被包装流会怎么样,这个敞开流有程序吗?而实际上咱们习惯都是两个流都敞开的。
首先咱们来做一个简略的试验,基于第一个问题的代码上减少手动减少敞开流的代码,那么就有两种程序:
1.先敞开被包装流(失常没异样抛出)
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class IOTest {
public static void main(String[] args) throws IOException {
FileOutputStream fileOutputStream = new FileOutputStream("c:\\a.txt");
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
bufferedOutputStream.write("test write something".getBytes());
bufferedOutputStream.flush();
fileOutputStream.close();//先敞开被包装流
bufferedOutputStream.close();
}
}
2.先敞开包装流(失常没异样抛出)
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class IOTest {
public static void main(String[] args) throws IOException {
FileOutputStream fileOutputStream = new FileOutputStream("c:\\a.txt");
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
bufferedOutputStream.write("test write something".getBytes());
bufferedOutputStream.flush();
bufferedOutputStream.close();//先敞开包装流
fileOutputStream.close();
}
}
上述两种写法都没有问题,咱们曾经晓得bufferedOutputStream.close();
会主动调用fileOutputStream.close();
办法,那么这个办法是怎么执行的呢?咱们又看看FileOutputStream
的源码:
public void close() throws IOException {
synchronized (closeLock) {
if (closed) {
return;
}
closed = true;
}
...
能够看出它采纳同步锁,而且应用了敞开标记,如果曾经敞开了则不会再次操作,所以屡次调用不会呈现问题。
如果没有看过参考文章,我可能就会断下结论,敞开流不须要思考程序
咱们看下上面的代码(批改自参考文章):
import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
public class IOTest {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("c:\\a.txt");
OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF-8");
BufferedWriter bw = new BufferedWriter(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() throws IOException {
synchronized (lock) {
if (out == null) {
return;
}
try {
flushBuffer();
} finally {
out.close();
out = null;
cb = null;
}
}
}
外面调用了flushBuffer()
办法,也是抛异样中的错误方法:
void flushBuffer() throws IOException {
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() throws IOException {
if (out == null)
throw new IOException("Stream closed");
}
void flushBuffer() throws IOException {
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() throws IOException {
synchronized (lock) {
if (out == null) {
return;
}
try {
flushBuffer();
} finally {
out.close();
out = null;
cb = null;
}
}
}
finally中就有把out置为null的代码,所以有时候不须要本人手动置为null。(集体倡议还是写一下,不差多少执行工夫)
起源:blog.csdn.net/maxwell_nc/article/details/49151005
近期热文举荐:
1.1,000+ 道 Java面试题及答案整顿(2022最新版)
2.劲爆!Java 协程要来了。。。
3.Spring Boot 2.x 教程,太全了!
4.别再写满屏的爆爆爆炸类了,试试装璜器模式,这才是优雅的形式!!
5.《Java开发手册(嵩山版)》最新公布,速速下载!
感觉不错,别忘了顺手点赞+转发哦!
发表回复