首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java final、finally、finalize 深度辨析:修飾符、異常塊與 GC 鉤子

     編輯:Java 相關知識

三個長相相似、語意迥異的關鍵字/方法,是面試高頻考點,也是日常開發易混淆處。本文從語法層面、執行時機、JVM 實作、最佳實踐四個維度徹底釐清。

快速對照表

關鍵字/方法類別作用對象核心語意執行時機
final修飾符類、方法、變量、參數不可變/不可繼承/不可覆寫編譯期/類載入期
finally程式塊try-catch 結構無論是否異常必執行清理碼運行期 (try 結束前)
finalize()Object 方法物件實例GC 前釋放非記憶體資源GC 回收前 (不確定、已棄用)

1. final 修飾符

四大用法

  • 修飾類: `public final class Immutable {}` → 不可被繼承(如 String, Integer)。
  • 修飾方法: `public final void critical() {}` → 子類不可覆寫,但可**重載**。
    ⚠️ private 方法隱式 final,無意義再加。
  • 修飾變量 (欄位/區域/參數):
    • 基本型別:值不可變。
    • 引用型別:引用不可變,**但物件內部狀態仍可修改**。
    • 編譯期常量:`static final int MAX = 100;` → 常量池內聯,效能最優。
  • 修飾參數: `void method(final List list)` → 方法內不可重新賦值 `list = new ArrayList<>()`,但可 `list.add()`。

效能影響

  • JIT 可對 final 方法進行**內聯優化**(devirtualization)。
  • final 欄位構造函數結束前必須初始化,保證發佈安全性(JMM happens-before)。

2. finally 異常處理塊

執行保證

try {
    return 1;
} finally {
    System.out.println("always"); // 必輸出
    // return 2; // 若這裡 return,會覆蓋 try 的返回值(反模式)
}

不執行的極端情況: `System.exit(0)`、JVM Crash、無限迴圈/死鎖導致執行緒不退出 try。

資源管理最佳實踐 (Java 7+)

// try-with-resources 自動關閉,優於手動 finally
try (InputStream in = Files.newInputStream(path);
     BufferedReader br = new BufferedReader(new InputStreamReader(in))) {
    return br.readLine();
} // 隱式 finally 關閉資源,異常自動抑制

3. finalize() 終結方法

機制與缺陷

  • GC 回收不可達物件前,若物件覆寫 finalize(),JVM 會將其放入 **Finalizer 佇列**,由低優先級 **Finalizer 線程** 執行。
  • 不可靠: 不保證執行時間、甚至不保證執行(JVM 關閉時可跳過)。
  • 效能殺手: 物件需經歷兩次 GC(第一次發現 finalize、入佇列;第二次真正回收),延長存活時間,增加 GC 壓力。
  • 異常吞噬: finalize 拋出異常會被忽略,導致資源洩漏無感知。

Java 9+ 棄用與替代方案

// 1. try-with-resources (首選)
try (Resource r = acquire()) { ... }

// 2. Cleaner API (Java 9+,非阻塞、可預測)
public class ResourceHolder implements AutoCloseable {
    private static final Cleaner cleaner = Cleaner.create();
    private final Cleaner.Cleanable cleanable;
    private final Resource resource;

    public ResourceHolder(Resource r) {
        this.resource = r;
        this.cleanable = cleaner.register(this, r::close);
    }
    @Override public void close() { cleanable.clean(); }
}

// 3. PhantomReference + ReferenceQueue (底層控制)

綜合實戰場景

public final class DatabaseConnection implements AutoCloseable {
    private final Connection conn; // final 欄位:引用不可變

    public DatabaseConnection(String url) throws SQLException {
        this.conn = DriverManager.getConnection(url);
    }

    public void execute(String sql) { ... } // final 方法不可覆寫

    @Override
    public void close() throws SQLException { // try-with-resources 契約
        if (conn != null && !conn.isClosed()) conn.close();
    }

    // ❌ 絕不覆寫 finalize()
}

記憶口訣

final 是承諾(編譯期鎖死);finally 是保障(運行期兜底);finalize 是遺囑(GC 前念一次,不靠譜,別用)。

徹底理解三者差異,能寫出更安全、高效、符合現代 Java 風格的代碼。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved