分享 23周 Java 关于IO流部分用法

1,099 阅读4分钟

本文章基于本人工作中碰到一些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 <code>flush</code> 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.
     * <p>
     * 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.
     * <p>
     * The <code>flush</code> method of <code>OutputStream</code> 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 <code>close</code>
     * is that it closes the output stream. A closed stream cannot perform
     * output operations and cannot be reopened.
     * <p>
     * The <code>close</code> method of <code>OutputStream</code> 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.
     *
     * <p> 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
     * <em>mark</em> 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直接调用