Java之如何關(guān)閉流
我們深知在操作Java流對(duì)象后要將流關(guān)閉,但往往事情不盡人意,大致有以下幾種不能一定將流關(guān)閉的寫法:
1.在try中關(guān)流,而沒(méi)在finally中關(guān)流
try { OutputStream out = new FileOutputStream(""); // ...操作流代碼 out.close(); } catch (Exception e) { e.printStackTrace(); }
正確寫法:
OutputStream out = null; try { out = new FileOutputStream(""); // ...操作流代碼 } catch (Exception e) { e.printStackTrace(); } finally { try { if (out != null) { out.close(); } } catch (Exception e) { e.printStackTrace(); } }
2.在關(guān)閉多個(gè)流時(shí)因?yàn)橄勇闊⑺嘘P(guān)流的代碼丟到一個(gè)try中
OutputStream out = null; OutputStream out2 = null; try { out = new FileOutputStream(""); out2 = new FileOutputStream(""); // ...操作流代碼 } catch (Exception e) { e.printStackTrace(); } finally { try { if (out != null) { out.close();// 如果此處出現(xiàn)異常,則out2流沒(méi)有被關(guān)閉 } if (out2 != null) { out2.close(); } } catch (Exception e) { e.printStackTrace(); } }
正確寫法:
OutputStream out = null; OutputStream out2 = null; try { out = new FileOutputStream(""); out2 = new FileOutputStream(""); // ...操作流代碼 } catch (Exception e) { e.printStackTrace(); } finally { try { if (out != null) { out.close();// 如果此處出現(xiàn)異常,則out2流也會(huì)被關(guān)閉 } } catch (Exception e) { e.printStackTrace(); } try { if (out2 != null) { out2.close(); } } catch (Exception e) { e.printStackTrace(); } }
3.在循環(huán)中創(chuàng)建流
在循環(huán)外關(guān)閉,導(dǎo)致關(guān)閉的是最后一個(gè)流
OutputStream out = null; try { for (int i = 0; i < 10; i++) { out = new FileOutputStream(""); // ...操作流代碼 } } catch (Exception e) { e.printStackTrace(); } finally { try { if (out != null) { out.close(); } } catch (Exception e) { e.printStackTrace(); } }
正確寫法:
for (int i = 0; i < 10; i++) { OutputStream out = null; try { out = new FileOutputStream(""); // ...操作流代碼 } catch (Exception e) { e.printStackTrace(); } finally { try { if (out != null) { out.close(); } } catch (Exception e) { e.printStackTrace(); } } }
4.在Java7中
關(guān)閉流這種繁瑣的事情再也不用我們自己敲代碼了
try (OutputStream out = new FileOutputStream("")){ // ...操作流代碼 } catch (Exception e) { e.printStackTrace(); }
只要實(shí)現(xiàn)的自動(dòng)關(guān)閉接口(Closeable)的類都可以在try結(jié)構(gòu)體上定義,java會(huì)自動(dòng)幫我們關(guān)閉,及時(shí)在發(fā)生異常的情況下也會(huì)。可以在try結(jié)構(gòu)體上定義多個(gè),用分號(hào)隔開(kāi)即可,如:
try (OutputStream out = new FileOutputStream("");OutputStream out2 = new FileOutputStream("")){ // ...操作流代碼 } catch (Exception e) { throw e; }
注:Android SDK 20版本對(duì)應(yīng)java7,低于20版本無(wú)法使用try-catch-resources自動(dòng)關(guān)流。
5.內(nèi)存流可以不用關(guān)閉(關(guān)與不關(guān)都可以,沒(méi)影響)
ByteArrayOutputStream和ByteArrayInputStream其實(shí)是偽裝成流的字節(jié)數(shù)組(把它們當(dāng)成字節(jié)數(shù)據(jù)來(lái)看就好了),他們不會(huì)鎖定任何文件句柄和端口,如果不再被使用,字節(jié)數(shù)組會(huì)被垃圾回收掉,所以不需要關(guān)閉。
6.使用裝飾流時(shí),只需要關(guān)閉最后面的裝飾流即可
裝飾流是指通過(guò)裝飾模式實(shí)現(xiàn)的java流,又稱為包裝流,裝飾流只是為原生流附加額外的功能(或效果),java中的緩沖流、橋接流也是屬于裝飾流。
InputStream is=new FileInputStream("C:\\Users\\tang\\Desktop\\記事本.txt"); InputStreamReader isr=new InputStreamReader(is); BufferedReader br = new BufferedReader(isr); String string = br.readLine(); System.out.println(string); try { br.close();//只需要關(guān)閉最后的br即可 } catch (Exception e) { e.printStackTrace(); }
裝飾流關(guān)閉時(shí)會(huì)調(diào)用原生流關(guān)閉,請(qǐng)看源碼:
//BufferedReader.java public void close() throws IOException { synchronized (lock) { if (in == null) return; try { in.close();//這里的in就是原生流 } finally { in = null; cb = null; } } }
//InputStreamReader.java public void close() throws IOException { sd.close();//這里的sd就是原生流的解碼器(StreamDecoder),解碼器的close會(huì)調(diào)用原生流的close }
有這樣層層關(guān)閉的機(jī)制,我們就只需要關(guān)閉最外層的流就行了(甚至博主認(rèn)為,其實(shí)只關(guān)閉最里層的流也可以,因?yàn)檠b飾流并不持有任何文件句柄和端口,它和內(nèi)存流一樣是個(gè)“假流”,當(dāng)然這只是我的個(gè)人推理,不敢保證只關(guān)閉最里層的流一定沒(méi)有問(wèn)題)。
7.關(guān)閉流時(shí)的順序問(wèn)題
兩個(gè)不相干的流的關(guān)閉順序沒(méi)有任何影響,如:
out1 = new FileOutputStream(""); out2 = new FileOutputStream(""); //這里的out1和out2誰(shuí)先關(guān)誰(shuí)后關(guān)都一樣,沒(méi)有任何影響
如果兩個(gè)流有依賴關(guān)系,那么你可以像上面說(shuō)的,只關(guān)閉最外層的即可。
如果不嫌麻煩,非得一個(gè)個(gè)關(guān)閉,那么需要先關(guān)閉最里層,從里往外一層層進(jìn)行關(guān)閉。
為什么不能從外層往里層逐步關(guān)閉?原因上面講裝飾流已經(jīng)講的很清楚了,關(guān)閉外層時(shí),內(nèi)層的流其實(shí)已經(jīng)同時(shí)關(guān)閉了,你再去關(guān)內(nèi)層的流,就會(huì)報(bào)錯(cuò)
至于網(wǎng)上說(shuō)的先聲明先關(guān)閉,就是這個(gè)道理,先聲明的是內(nèi)層,最先申明的是最內(nèi)層,最后聲明的是最外層。
分割線-----------------------------
其實(shí)jdk8版的順序隨便打亂關(guān)閉都不會(huì)報(bào)錯(cuò),因?yàn)樽罾锩娴挠信袛?,如果流已?jīng)關(guān)閉直接return)。
可以看FileInputStream源碼:
public void close() throws IOException { synchronized (closeLock) { if (closed) { return; } closed = true; } if (channel != null) { channel.close(); } fd.closeAll(new Closeable() { public void close() throws IOException { close0(); } }); }
其他jdk版本,博主時(shí)間有限沒(méi)有測(cè)試,各位還是遵循老辦法(分割線前面的)關(guān)閉吧。
8.深究為什么一定要關(guān)閉流的原因
一個(gè)流綁定了一個(gè)文件句柄(或網(wǎng)絡(luò)端口),如果流不關(guān)閉,該文件(或端口)將始終處于被鎖定(不能讀取、寫入、刪除和重命名)狀態(tài),占用大量系統(tǒng)資源卻沒(méi)有釋放。
9.推薦使用NIO的Files工具類替換FileInputStream和FileOutputStream
public static?List<String> readAllLines(Path path, Charset cs)//以字符流方式讀取所有行 public static Path write(Path path, Iterable<? extends CharSequence> lines, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?Charset cs, OpenOption... options)//以字符流方式寫入指定行 public static?byte[] readAllBytes(Path path)//以字節(jié)流方式讀取所有字節(jié) public static Path write(Path path, byte[] bytes, OpenOption... options)//以字節(jié)流方式寫入指定字節(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Maven介紹與配置+IDEA集成Maven+使用Maven命令小結(jié)
Maven是Apache軟件基金會(huì)的一個(gè)開(kāi)源項(xiàng)目,是一個(gè)優(yōu)秀的項(xiàng)目構(gòu)建管理工具,它用來(lái)幫助開(kāi)發(fā)者管理項(xiàng)目中的 jar,以及 jar 之間的依賴關(guān)系、完成項(xiàng)目的編譯、測(cè)試、打包和發(fā)布等工作,本文給大家介紹Maven介紹與配置+IDEA集成Maven+使用Maven命令,感興趣的朋友一起看看吧2024-01-01JAVA實(shí)戰(zhàn)練習(xí)之圖書(shū)管理系統(tǒng)實(shí)現(xiàn)流程
隨著網(wǎng)絡(luò)技術(shù)的高速發(fā)展,計(jì)算機(jī)應(yīng)用的普及,利用計(jì)算機(jī)對(duì)圖書(shū)館的日常工作進(jìn)行管理勢(shì)在必行,本篇文章手把手帶你用Java實(shí)現(xiàn)一個(gè)圖書(shū)管理系統(tǒng),大家可以在過(guò)程中查缺補(bǔ)漏,提升水平2021-10-10Idea如何使用Fast Request接口調(diào)試
這篇文章主要介紹了Idea如何使用Fast Request接口調(diào)試問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-11-11SpringBoot實(shí)現(xiàn)文件在線預(yù)覽功能的全過(guò)程
我們開(kāi)發(fā)業(yè)務(wù)系統(tǒng)的時(shí)候,經(jīng)常有那種文檔文件在線預(yù)覽的需求,下面這篇文章主要給大家介紹了關(guān)于SpringBoot實(shí)現(xiàn)文件在線預(yù)覽功能的相關(guān)資料,需要的朋友可以參考下2021-11-11Java 常見(jiàn)的幾種內(nèi)存溢出異常的原因及解決
這篇文章主要介紹了Java 常見(jiàn)的幾種內(nèi)存溢出異常及解決,幫助大家更好的理解和學(xué)習(xí)使用Java,感興趣的朋友可以了解下2021-04-04Spring通過(guò)配置文件管理Bean對(duì)象的方法
這篇文章主要介紹了Spring通過(guò)配置文件管理Bean對(duì)象的相關(guān)知識(shí),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-07-07