在學習 Java IO 串流時,許多開發者會遇到一個經典問題:將資源關閉語句 file.close() 放在 finally 區塊中時,編譯器卻報 IOException 錯誤。本文將深入分析這個問題的根本原因,並提供兩種主流解決方案。
以下程式碼看似符合「資源必須在 finally 中關閉」的最佳實踐,但編譯卻會失敗:
import java.io.*;
public class InputFile {
public static void main(String[] args) {
int a = 0;
FileInputStream file = null;
try {
file = new FileInputStream("G:\\java\\InputFile\\src\\InputFile.java");
while ((a = file.read()) != -1) {
System.out.print((char) a);
}
} catch (IOException e) {
System.out.println("檔案讀取錯誤");
System.exit(-1);
} finally {
if (file != null) {
file.close(); // 編譯錯誤:Unhandled exception IOException
}
}
}
}
問題的核心在於 FileInputStream 的宣告位置與 close() 方法的受檢異常特性:
finally 可見,file 必須宣告在 try 之前並初始化為 null。new FileInputStream(...) 失敗(拋出 FileNotFoundException,繼承自 IOException),file 仍為 null,if (file != null) 判斷為假,不會執行 close()。finally 中呼叫 file.close() 時,該方法宣告拋出 IOException,Java 強制要求處理此受檢異常。file != null 時才呼叫 close(),但編譯器僅靜態分析,認為 close() 可能拋出異常且未被捕獲。最直接的修正方式,是在 finally 區塊內部再包一層 try-catch 專門處理關閉資源時的異常:
finally {
if (file != null) {
try {
file.close();
} catch (IOException e) {
// 記錄日誌或忽略,關閉失敗通常不影響主流程
e.printStackTrace();
}
}
}
這樣滿足了編譯器對受檢異常的處理要求,同時保證資源一定會被關閉。
Java 7 引入的 try-with-resources 機制,能自動管理實作 AutoCloseable 介面的資源,程式碼更簡潔、安全:
import java.io.*;
public class InputFile {
public static void main(String[] args) {
int a = 0;
// 資源直接宣告在 try() 圓括號內
try (FileInputStream file = new FileInputStream("G:\\java\\InputFile\\src\\InputFile.java")) {
while ((a = file.read()) != -1) {
System.out.print((char) a);
}
// 無需手動呼叫 close(),JVM 會自動在區塊結束時關閉
} catch (IOException e) {
System.out.println("檔案讀取錯誤");
System.exit(-1);
}
}
}
優點:
getSuppressed() 取得)nulltry (Res r1 = ...; Res r2 = ...) { }try 區塊外時,finally 中關閉必須巢狀 try-catch。null),否則編譯器會報「variable might not have been initialized」。close() 拋出 IOException 屬受檢異常,必須處理。