nio java,java io流不關閉_Java IO流關閉問題的深入研究

 2023-11-30 阅读 26 评论 0

摘要:前幾天看了一篇文章(見參考文章),自己動手試了下,發現有些不一樣結論,作博客記錄下,本文主要研究兩個問題:包裝流的close方法是否會自動關閉被包裝的流?關閉流方法是否有順序?nio java?包裝流的close方法是否會自動關閉被包

前幾天看了一篇文章(見參考文章),自己動手試了下,發現有些不一樣結論,作博客記錄下,本文主要研究兩個問題:

包裝流的close方法是否會自動關閉被包裝的流?

關閉流方法是否有順序?

nio java?包裝流的close方法是否會自動關閉被包裝的流?

平時我們使用輸入流和輸出流一般都會使用buffer包裝一下,

直接看下面代碼(這個代碼運行正常,不會報錯)

1 importjava.io.BufferedOutputStream;2 importjava.io.FileOutputStream;3 importjava.io.IOException;4

JAVA io?5

6 public classIOTest {7

8 public static void main(String[] args) throwsIOException {9

10 FileOutputStream fileOutputStream = new FileOutputStream("c:\\a.txt");11 BufferedOutputStream bufferedOutputStream = newBufferedOutputStream(fileOutputStream);12

java 線程池,13 bufferedOutputStream.write("test write something".getBytes());14 bufferedOutputStream.flush();15

16 //從包裝流中關閉流

17 bufferedOutputStream.close();18 }19

20 }

java.util,下面我們來研究下這段代碼的bufferedOutputStream.close();方法是否調用了fileOutputStream.close();

先看BufferedOutputStream源代碼:

public class BufferedOutputStream extends FilterOutputStream { ...

1

java泛型,可以看到它繼承FilterOutputStream,并且沒有重寫close方法,

所以直接看FilterOutputStream的源代碼:

1 public void close() throwsIOException {2 try{3 flush();4 } catch(IOException ignored) {5 }6 out.close();7 }

跟蹤out(FilterOutputStream中):

java arraylist?1 protectedOutputStream out;2

3 publicFilterOutputStream(OutputStream out) {4 this.out =out;5 }

再看看BufferedOutputStream中:

1 publicBufferedOutputStream(OutputStream out) {2 this(out, 8192);3 }4

java教程、5 public BufferedOutputStream(OutputStream out, intsize) {6 super(out);7 if (size <= 0) {8 throw new IllegalArgumentException("Buffer size <= 0");9 }10 buf = new byte[size];11 }

可以看到BufferedOutputStream調用super(out);,也就是說,out.close();調用的是通過BufferedOutputStream傳入的被包裝的流,這里就是FileOutputStream。

我們在看看其他類似的,比如BufferedWriter的源代碼:

1 public void close() throwsIOException {2 synchronized(lock) {3 if (out == null) {4 return;5 }6 try{7 flushBuffer();8 } finally{9 out.close();10 out = null;11 cb = null;12 }13 }14 }

java中static、通過觀察各種流的源代碼,可得結論:包裝的流都會自動調用被包裝的流的關閉方法,無需自己調用。

關閉流方法是否有順序?

由上面的結論,就會產生一個問題:如果手動關閉被包裝流會怎么樣,這個關閉流有順序嗎?而實際上我們習慣都是兩個流都關閉的。

首先我們來做一個簡單的實驗,基于第一個問題的代碼上增加手動增加關閉流的代碼,那么就有兩種順序:

1.先關閉被包裝流(正常沒異常拋出)

1 importjava.io.BufferedOutputStream;2 importjava.io.FileOutputStream;3 importjava.io.IOException;4

5

6 public classIOTest {7

8 public static void main(String[] args) throwsIOException {9

10 FileOutputStream fileOutputStream = new FileOutputStream("c:\\a.txt");11 BufferedOutputStream bufferedOutputStream = newBufferedOutputStream(fileOutputStream);12

13 bufferedOutputStream.write("test write something".getBytes());14 bufferedOutputStream.flush();15

16 fileOutputStream.close();//先關閉被包裝流

17 bufferedOutputStream.close();18 }19

20 }

2.先關閉包裝流(正常沒異常拋出)

1 importjava.io.BufferedOutputStream;2 importjava.io.FileOutputStream;3 importjava.io.IOException;4

5

6 public classIOTest {7

8 public static void main(String[] args) throwsIOException {9

10 FileOutputStream fileOutputStream = new FileOutputStream("c:\\a.txt");11 BufferedOutputStream bufferedOutputStream = newBufferedOutputStream(fileOutputStream);12

13 bufferedOutputStream.write("test write something".getBytes());14 bufferedOutputStream.flush();15

16

17 bufferedOutputStream.close();//先關閉包裝流

18 fileOutputStream.close();19

20 }21

22 }

上述兩種寫法都沒有問題,我們已經知道bufferedOutputStream.close();會自動調用fileOutputStream.close();方法,那么這個方法是怎么執行的呢?我們又看看

1 FileOutputStream的源碼:2

3 public void close() throwsIOException {4 synchronized(closeLock) {5 if(closed) {6 return;7 }8 closed = true;9 }10

11 ...

可以看出它采用同步鎖,而且使用了關閉標記,如果已經關閉了則不會再次操作,所以多次調用不會出現問題。

如果沒有看過參考文章,我可能就會斷下結論,關閉流不需要考慮順序

我們看下下面的代碼(修改自參考文章):

1 importjava.io.BufferedWriter;2 importjava.io.FileOutputStream;3 importjava.io.IOException;4 importjava.io.OutputStreamWriter;5

6 public classIOTest {7

8 public static void main(String[] args) throwsIOException {9

10 FileOutputStream fos = new FileOutputStream("c:\\a.txt");11 OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF-8");12 BufferedWriter bw = newBufferedWriter(osw);13 bw.write("java IO close test");14

15 //從內帶外順序順序會報異常

16 fos.close();17 osw.close();18 bw.close();19

20 }21

22 }

會拋出Stream closed的IO異常:

1 Exception in thread "main"java.io.IOException: Stream closed2 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() throwsIOException {2 synchronized(lock) {3 if (out == null) {4 return;5 }6 try{7 flushBuffer();8 } finally{9 out.close();10 out = null;11 cb = null;12 }13 }14 }

里面調用了flushBuffer()方法,也是拋異常中的錯誤方法:

1 void flushBuffer() throwsIOException {2 synchronized(lock) {3 ensureOpen();4 if (nextChar == 0)5 return;6 out.write(cb, 0, nextChar);7 nextChar = 0;8 }9 }

可以看到很大的一行

1 out.write(cb, 0, nextChar);

這行如果在流關閉后執行就會拋IO異常,

有時候我們會寫成:

1 fos.close();2 fos = null;3 osw.close();4 osw = null;5 bw.close();6 bw = null;

這樣也會拋異常,不過是由于flushBuffer()中ensureOpen()拋的,可從源碼中看出:

1 private void ensureOpen() throwsIOException {2 if (out == null)3 throw new IOException("Stream closed");4 }5

6

7 void flushBuffer() throwsIOException {8 synchronized(lock) {9 ensureOpen();10 if (nextChar == 0)11 return;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() throwsIOException {2 synchronized(lock) {3 if (out == null) {4 return;5 }6 try{7 flushBuffer();8 } finally{9 out.close();10 out = null;11 cb = null;12 }13 }14 }

finally中就有把out置為null的代碼,所以有時候不需要自己手動置為null。(個人建議還是寫一下,不差多少執行時間)

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://hbdhgg.com/2/186687.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 匯編語言學習筆記 Inc. 保留所有权利。

底部版权信息