本文章基于本人工作中碰到一些IO流使用和分析(结合源码),基于JDK1.8。

嵌套IO

部分教程,需要按照顺序一个层一层关闭流

FileInputStream fi;
InputStreamReader ir;
BufferedReader reader;
try{
File file = FileUtils.getFileObj(localPath, tbClearBatchFileLogDto.getRecv_filename());
fi = new FileInputStream(file), "UTF_8");
ir = new InputStreamReader(fi);
reader = new BufferedReader(ir);
//处理业务逻辑
......
}catch(Exception e){
//异常处理
.....
}finally{
try{
//按照包装流关闭,后被包装流关闭的顺序
if(fi!=null){
fi.close();
}
if(ir!=null){
ir.close();
}
if(reader!=null){
reader.close();
}
}catch (IOException e){
e.printStackTrace();
}
}

复制代码

其实按照目前版本,只需要关闭最外层的流即可,即如下

FileInputStream fi;
InputStreamReader ir;
BufferedReader reader;
try{
File file = FileUtils.getFileObj(localPath, tbClearBatchFileLogDto.getRecv_filename());
fi = new FileInputStream(file), "UTF_8");
ir = new InputStreamReader(fi);
reader = new BufferedReader(ir);
//处理业务逻辑
......
}catch(Exception e){
//异常处理
.....
}finally{
try{
//按照包装流关闭,后被包装流关闭的顺序
if(reader!=null){
reader.close();
}
}catch (IOException e){
e.printStackTrace();
}
}

复制代码

其实我们查看BufferedReader源码可以知道,调用BufferedReader的close会再调用被包装流的close。

public BufferedReader(Reader in, int sz) {
super(in);
if (sz <= 0)
throw new IllegalArgumentException("Buffer size <= 0");
this.in = in;
cb = new char[sz];
nextChar = nChars = 0;
}
public void close() throws IOException {
synchronized (lock) {
if (in == null)
return;
try {
in.close();
} finally {
in = null;
cb = null;
}
}
}

复制代码

输出包装流关闭

目前很多包装流如OutputStreamWriter、PrintWriter等教程中,都要求在执行close()之前,执行一下flush()。 我们可以先看看OutputStream对于这两个函数说明

/**
* Flushes this output stream and forces any buffered output bytes
* to be written out. The general contract of flush is
* that calling it is an indication that, if any bytes previously
* written have been buffered by the implementation of the output
* stream, such bytes should immediately be written to their
* intended destination.
* 
* If the intended destination of this stream is an abstraction provided by
* the underlying operating system, for example a file, then flushing the
* stream guarantees only that bytes previously written to the stream are
* passed to the operating system for writing; it does not guarantee that
* they are actually written to a physical device such as a disk drive.
* 
* The flush method of OutputStream does nothing.
*
* @exception IOException if an I/O error occurs.
*/
public void flush() throws IOException {
}
/**
* Closes this output stream and releases any system resources
* associated with this stream. The general contract of close
* is that it closes the output stream. A closed stream cannot perform
* output operations and cannot be reopened.
* 
* The close method of OutputStream does nothing.
*
* @exception IOException if an I/O error occurs.
*/
public void close() throws IOException {
}

复制代码

flush:刷新输出流并强制所有缓冲的字节输出写入。 close:关闭此输出流并释放所有系统资源。

但是我非常好奇,究竟我们close时候底层会不会执行flush。

PrintWriter out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(), CHARSET));

复制代码

我们以上面代码为例子分析一下。

PrintWriter的close是没有调用flush

public void close() {
try {
synchronized (lock) {
if (out == null)
return;
out.close();
out = null;
}
}
catch (IOException x) {
trouble = true;
}
}

复制代码

OutputStreamWriter的close是没有调用flush

public void close() throws IOException {
se.close();
}

复制代码

而socket获取getOutputStream获取对象需要按照实际实现类来决定,我们以最常见的实现来说明

ByteArrayOutputStream
public void close() throws IOException {
}

复制代码

FilterOutputStream
public void close() throws IOException {
try (OutputStream ostream = out) {
flush();
}
}

复制代码

BufferedOutputStream 继承FilterOutputStream,未重写close

以上各个实现类初步来看,无能完全判断close函数就一定调用flush函数。所以为了降低风险,我们调用close函数之前还是调用flush函数。而且从close实现来看,close关闭的资源还需按照各实现类具体所使用资源,然后自行自行关闭操作。

流的自动关闭

总结: (1)自动关闭的资源对象必须实现Closeable或者AutoCloseable;Closeable继承于AutoCloseable接口,只有一个close方法

public interface Closeable extends AutoCloseable {
/**
* Closes this stream and releases any system resources associated
* with it. If the stream is already closed then invoking this
* method has no effect.
*
* 
 As noted in {@link AutoCloseable#close()}, cases where the
* close may fail require careful attention. It is strongly advised
* to relinquish the underlying resources and to internally
* mark the {@code Closeable} as closed, prior to throwing
* the {@code IOException}.
*
* @throws IOException if an I/O error occurs
*/
public void close() throws IOException;
}

复制代码

(2)通常过重写close方法来实现资源自动关闭;

(3)自动关闭资源使用try特性,无需显性调用关闭资源方法;于finally一致,最后都会执行。如下:

public static void main(String[] args) {
try(InputStream inputStream = new FileInputStream("f:/bb.txt")) {
}catch (Exception e){
e.printStackTrace();
}


复制代码

查看FileInputStream的源码,FileInputStream继承InputStream,InputStream实现Closeable。而本省IO流就需要实现close方法,所以直接可以被try直接调用