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

Java final、finally、finalize 深度解析與常見誤區

     編輯:關於JSP

核心概念速覽

關鍵字類型核心作用
final修飾符限制繼承、重寫、賦值;構建不可變對象
finally異常處理塊保證資源釋放代碼必執行
finalizeObject 方法垃圾回收前清理非 Java 資源(已棄用)

final 修飾符深度剖析

1. 修飾類:不可繼承

public final class ImmutableClass {
    // 無法被繼承,所有方法隱式 final
}

注意:final 類不能同時被 abstract 修飾,因為抽象類旨在被繼承。

2. 修飾方法:不可重寫

public class Parent {
    public final void doSomething() { /* 子類不可覆蓋 */ }
}

私有方法隱式 final,子類無法覆蓋(只能定義同名新方法)。

3. 修飾變量:引用不可變


// 基本類型:值不可變
final int MAX_SIZE = 100;

// 引用類型:引用不可變,但對象內部狀態可變
final List<String> list = new ArrayList<>();
list.add("Hello"); // 合法
// list = new ArrayList<>(); // 編譯錯誤:引用不可重新賦值

4. final 變量初始化規則(重點糾正常見誤區)

錯誤觀點:「final 變量只能在聲明處或構造函數中初始化,二選一」

正確規則:final 變量必須在構造完成前顯式初始化一次,具體位置靈活:

  • 聲明時直接賦值
  • 實例初始化塊 { ... }
  • 構造函數中
  • 靜態初始化塊 static { ... }(僅靜態 final 變量)
public class FinalInitDemo {
    final int a = 10;                    // 1. 聲明處
    final int b;                         // 2. 必須在構造器/初始化塊賦值
    final int c;                         // 3. 實例初始化塊
    { c = 30; }
    static final int d;                  // 4. 靜態初始化塊
    static { d = 40; }

    public FinalInitDemo() {
        b = 20;                          // 構造函數賦值
    }
}

5. 方法參數 final 的真實意義

常見誤區:「防止方法內修改參數影響外部變量」

事實:Java 傳值調用,基本類型傳值副本,引用類型傳引用副本。方法內重新賦值參數(param = new Obj())絕不影響外部實參;修改對象內部狀態(param.setField())無論是否 final 都會影響外部。

真實用途:允許匿名內部類/Lambda 捕獲該參數。編譯器要求被捕獲的局部變量/參數實際最終不變,final 保證了生命週期延長至內部類實例存活期間。

public void executeTask(final String config) {
    new Thread(() -> {
        // config 被捕獲,必須 final 或 effectively final
        System.out.println("Config: " + config);
    }).start();
}

finally 塊:資源管理的基石

經典嵌套模式(Java 7 前)

Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
    conn = getConnection();
    try {
        stmt = conn.createStatement();
        try {
            rs = stmt.executeQuery(sql);
            // 處理結果集
        } finally {
            if (rs != null) rs.close();
        }
    } finally {
        if (stmt != null) stmt.close();
    }
} finally {
    if (conn != null) conn.close();
}

現代最佳實踐:Try-with-resources (Java 7+)

try (Connection conn = getConnection();
     Statement stmt = conn.createStatement();
     ResultSet rs = stmt.executeQuery(sql)) {
    // 自動按相反順序調用 close()
} catch (SQLException e) {
    // 異常處理
}

優勢:代碼簡潔、自動處理異常抑制、編譯器生成正確的資源釋放邏輯。

finalize() 方法:已棄用的清理機制

基本特性

  • 屬於 Object,簽名 protected void finalize() throws Throwable
  • GC 回收對象前可能調用,但不保證調用時間、順序、甚至是否調用
  • JDK 9 標記為 @Deprecated(since="9", forRemoval=true)

為何不可靠?

  1. 調用不確定:對象變為不可達到 GC 執行 finalize 間隔不可控;OOM 時可能根本不調用。
  2. 性能開銷:帶 finalize 的對象需經歷兩次 GC 週期(首次進入 Finalizer 佇列,二次才真正回收)。
  3. 線程安全風險:Finalizer 由單線程執行,阻塞會導致內存積壓。

替代方案

場景推薦方案
文件/網絡/數據庫連接Try-with-resources / 顯式 close()
原生內存/Off-heap 資源Cleaner / PhantomReference (Java 9+)
緩存/觀察者模式註冊弱引用 WeakReference + ReferenceQueue

總結對比

維度finalfinallyfinalize
語法角色修飾符代碼塊方法
編譯期檢查強制強制(配合 try)無
運行期保證引用不變代碼必執行無保證
主要用途不可變性、線程安全資源釋放歷史遺留清理(棄用)
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved