Java之如何關閉流
我們深知在操作Java流對象后要將流關閉,但往往事情不盡人意,大致有以下幾種不能一定將流關閉的寫法:
1.在try中關流,而沒在finally中關流
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.在關閉多個流時因為嫌麻煩將所有關流的代碼丟到一個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();// 如果此處出現異常,則out2流沒有被關閉
}
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();// 如果此處出現異常,則out2流也會被關閉
}
} catch (Exception e) {
e.printStackTrace();
}
try {
if (out2 != null) {
out2.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}3.在循環(huán)中創(chuàng)建流
在循環(huán)外關閉,導致關閉的是最后一個流
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中
關閉流這種繁瑣的事情再也不用我們自己敲代碼了
try (OutputStream out = new FileOutputStream("")){
// ...操作流代碼
} catch (Exception e) {
e.printStackTrace();
}只要實現的自動關閉接口(Closeable)的類都可以在try結構體上定義,java會自動幫我們關閉,及時在發(fā)生異常的情況下也會??梢栽趖ry結構體上定義多個,用分號隔開即可,如:
try (OutputStream out = new FileOutputStream("");OutputStream out2 = new FileOutputStream("")){
// ...操作流代碼
} catch (Exception e) {
throw e;
}注:Android SDK 20版本對應java7,低于20版本無法使用try-catch-resources自動關流。
5.內存流可以不用關閉(關與不關都可以,沒影響)
ByteArrayOutputStream和ByteArrayInputStream其實是偽裝成流的字節(jié)數組(把它們當成字節(jié)數據來看就好了),他們不會鎖定任何文件句柄和端口,如果不再被使用,字節(jié)數組會被垃圾回收掉,所以不需要關閉。
6.使用裝飾流時,只需要關閉最后面的裝飾流即可
裝飾流是指通過裝飾模式實現的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();//只需要關閉最后的br即可
} catch (Exception e) {
e.printStackTrace();
}裝飾流關閉時會調用原生流關閉,請看源碼:
//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會調用原生流的close
}有這樣層層關閉的機制,我們就只需要關閉最外層的流就行了(甚至博主認為,其實只關閉最里層的流也可以,因為裝飾流并不持有任何文件句柄和端口,它和內存流一樣是個“假流”,當然這只是我的個人推理,不敢保證只關閉最里層的流一定沒有問題)。
7.關閉流時的順序問題
兩個不相干的流的關閉順序沒有任何影響,如:
out1 = new FileOutputStream("");
out2 = new FileOutputStream("");
//這里的out1和out2誰先關誰后關都一樣,沒有任何影響如果兩個流有依賴關系,那么你可以像上面說的,只關閉最外層的即可。
如果不嫌麻煩,非得一個個關閉,那么需要先關閉最里層,從里往外一層層進行關閉。
為什么不能從外層往里層逐步關閉?原因上面講裝飾流已經講的很清楚了,關閉外層時,內層的流其實已經同時關閉了,你再去關內層的流,就會報錯
至于網上說的先聲明先關閉,就是這個道理,先聲明的是內層,最先申明的是最內層,最后聲明的是最外層。
分割線-----------------------------
其實jdk8版的順序隨便打亂關閉都不會報錯,因為最里面的有判斷,如果流已經關閉直接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版本,博主時間有限沒有測試,各位還是遵循老辦法(分割線前面的)關閉吧。
8.深究為什么一定要關閉流的原因
一個流綁定了一個文件句柄(或網絡端口),如果流不關閉,該文件(或端口)將始終處于被鎖定(不能讀取、寫入、刪除和重命名)狀態(tài),占用大量系統(tǒng)資源卻沒有釋放。
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é)
以上為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
詳解SpringBoot 解決攔截器注入Service為空問題
這篇文章主要介紹了詳解SpringBoot 解決攔截器注入Service為空問題的解決,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-06-06
Java使用JDBC向MySQL數據庫批次插入10W條數據(測試效率)
使用JDBC連接MySQL數據庫進行數據插入的時候,特別是大批量數據連續(xù)插入(100000),如何提高效率呢?今天小編通過本教程給大家介紹下2016-12-12

