C++ 支援堆疊物件 (自動析構) 與堆積物件 (手動 delete),因此需要析構函數釋放資源。Java 所有物件皆配置於堆積,由垃圾回收器 (GC) 管理記憶體,語言設計者認為不需要 C++ 風格析構函數,改提供 finalize() 機制。
| 特性 | C++ 析構函數 | Java finalize() |
|---|---|---|
| 呼叫時機 | 確定性:離開作用域或 delete 時立即執行 | 非確定性:GC 執行時才呼叫,甚至可能永不執行 |
| 記憶體釋放 | 程式設計師負責 (配合 delete/智慧型指標) | GC 自動負責,finalize 僅作非記憶體資源清理 |
| 例外處理 | 析構函數中拋出例外導致 terminate | finalize 拋出例外會被 GC 忽略,物件仍被回收 |
| 執行順序 | 嚴格遵循作用域巢狀/繼承鏈 | 無序,甚至跨執行緒並發執行 |
finalize()。finalize() 完成 (或拋出例外) → 物件真正回收。關鍵風險:若應用程式結束前 GC 未運行,finalize() 永不被呼叫 → 檔案控制代碼、資料庫連線、原生資源洩漏。
System.runFinalizersOnExit()程式結束時強制呼叫所有物件 finalize(),但會導致明顯關閉延遲,且已被棄用 (不安全,可能在其他執行緒使用物件時終結)。
// Java 7+ 推薦:AutoCloseable + try-with-resources
try (FileInputStream fis = new FileInputStream("data.txt");
BufferedReader br = new BufferedReader(new InputStreamReader(fis))) {
// 使用資源
} // 自動呼叫 close(),即使拋出例外也保證執行
這才是 Java 世界的「確定性析構」等價物,絕不依賴 finalize() 釋放關鍵資源。
finalize() 是妥協產物,非析構函數等價替代。現代 Java 開發應完全避免覆寫 finalize(),改用 AutoCloseable + try-with-resources 或 Cleaner (Java 9+) 管理非記憶體資源。