乐趣区

关于java:面试官IO-操作必须要手动关闭吗关闭流方法是否有顺序

前几天看了一篇文章,本人入手试了下,发现有些不一样论断,作博客记录下,本文次要钻研两个问题:

  • 包装流的 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 开发手册(嵩山版)》最新公布,速速下载!

感觉不错,别忘了顺手点赞 + 转发哦!

退出移动版