關閉 Java IO 流
1. 概述
確保在 Java IO 操作領域內正確關閉 IO 流非常重要。這對於資源管理和代碼穩健性非常重要。
在本教程中,我們將詳細探討為什麼需要關閉 IO 流。
2. IO流不關閉時會發生什麼?
在完成對 IO 流的所有操作後立即顯式關閉 IO 流始終是一個好習慣。忽略關閉它們可能會導致各種問題。
在本節中,我們將研究這些問題。
2.1.資源洩漏
每當我們打開一個IO steam時,總是會佔用一點系統資源。在調用 IO 流close()
方法之前,資源不會被釋放。
某些 IO 流實現可以在其finalize()
方法中自動關閉自身。每當垃圾收集器 (GC) 被觸發時,就會調用finalize()
方法。
但是,無法保證 GC 一定會被調用,也不保證何時會被調用。有可能在調用 GC 之前資源就耗盡了。因此,我們不應該僅僅依靠GC來回收系統資源。
2.2.數據損壞
我們經常將BufferedOutputStream
包裝在OutputStream
周圍,以提供緩衝功能,從而減少每個寫入操作的開銷。這是一種常見的做法,旨在提高寫入數據的性能。
BufferedOutputStream
中的內部緩衝區是臨時存儲數據的暫存區域。每當緩衝區達到一定大小或調用flush()
方法時,數據就會被寫入目的地。
當我們完成將數據寫入BufferedOutputStream
後,最後一塊數據可能尚未寫入目標,從而導致數據損壞。調用close()
方法會調用flush()
來寫入緩衝區中的剩餘數據。
2.3.文件鎖定
當我們使用FileOutputStream
將數據寫入文件時,某些操作系統(例如 Windows)會將文件保存在我們的應用程序中。這可以防止其他應用程序寫入甚至訪問該文件,直到FileOutputStream
關閉。
3. 關閉IO流
現在讓我們看一下關閉 Java IO 流的幾種方法。這些方法有助於避免我們上面討論的問題並確保適當的資源管理。
3.1. try-catch-finally
這是關閉 IO 流的傳統方式。我們在finally
塊中關閉IO流。這確保了無論操作是否成功都會調用close()
方法:
InputStream inputStream = null;
OutputStream outputStream = null;
try {
inputStream = new BufferedInputStream(wrappedInputStream);
outputStream = new BufferedOutputStream(wrappedOutputStream);
// Stream operations...
}
finally {
try {
if (inputStream != null)
inputStream.close();
}
catch (IOException ioe1) {
log.error("Cannot close InputStream");
}
try {
if (outputStream != null)
outputStream.close();
}
catch (IOException ioe2) {
log.error("Cannot close OutputStream");
}
}
正如我們所演示的, close()
方法也可能引發IOException
。因此,當關閉IO流時,我們必須在finally
塊中放置另一個try-catch
塊。當我們要處理大量的 IO 流時,這個過程就變得很麻煩。
3.2. Apache 公共 IO
Apache Commons IO 是一個多功能 Java 庫,為 IO 操作提供實用程序類和方法。
要使用它,我們在pom.xml
中包含以下依賴項:
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.13.0</version>
</dependency>
Apache Commons 庫簡化了複雜的任務,例如在finally
塊中關閉 IO 流:
InputStream inputStream = null;
OutputStream outputStream = null;
try {
inputStream = new BufferedInputStream(wrappedInputStream);
outputStream = new BufferedOutputStream(wrappedOutputStream);
// Stream operations...
}
finally {
IOUtils.closeQuietly(inputStream);
IOUtils.closeQuietly(outputStream);
}
IOUtils.closeQuietly()
有效地關閉 IO 流,無需進行 null 檢查並處理關閉過程中發生的異常。
除了IOUtils.closeQuietly()
之外,該庫還提供AutoCloseInputStream
類來自動關閉包裝的InputStream
:
InputStream inputStream = AutoCloseInputStream.builder().setInputStream(wrappedInputStream).get();
byte[] buffer = new byte[256];
while (inputStream.read(buffer) != -1) {
// Other operations...
}
上面的示例從InputStream
讀取數據。 AutoCloseInputStream
在到達輸入末尾時會自動關閉InputStream
,這是通過從InputStream
中的read()
方法獲取-1
來確定的。在這種情況下,我們甚至不需要顯式調用close()
方法。
3.3. try-with-resources
try-with-resources
塊是在 Java 7 中引入的。它被認為是關閉 IO 流的首選方式。
這種方法允許我們在try
語句中定義資源。資源是一個對象,當我們使用完它時必須關閉它。
例如,實現AutoClosable
接口的InputStream
和OutputStream
等類被用作資源。它們將在try-catch
塊之後自動關閉。這消除了在finally
塊中顯式調用close()
方法的需要:
try (BufferedInputStream inputStream = new BufferedInputStream(wrappedInputStream);
BufferedOutputStream outputStream = new BufferedOutputStream(wrappedOutputStream)) {
// Stream operations...
}
Java 9 中出現了進一步的進步,改進了try-with-resources
語法。我們可以在try-with-resources
塊之前聲明資源變量,並直接在try
語句中指定它們的變量名稱:
InputStream inputStream = new BufferedInputStream(wrappedInputStream);
OutputStream outputStream = new BufferedOutputStream(wrappedOutputStream);
try (inputStream; outputStream) {
// Stream operations...
}
4。結論
在本文中,我們研究了關閉 IO 流的各種策略,從在finally
塊中調用close()
方法的傳統方法到 Apache Commons IO 等庫提供的更簡化的方法以及try-with-resources
的優雅。
通過一系列不同的技術,我們可以選擇最適合我們的代碼庫的方法,並確保 IO 操作順利且無錯誤。
與往常一樣,本文中提供的源代碼可以在 GitHub 上獲取。