final 是 Java 中表達「不可變」語意的核心關鍵字,可修飾類、方法、變量(成員/局部/靜態)與參數。正確使用 final 能提升代碼安全性、可讀性,甚至助力 JVM 優化。本文系統梳理其四大應用場景與底層機制。
final 修飾的類不能被繼承,所有方法隱式為 final。java.lang.String、java.lang.Math、包裝類型。final。final 方法進行內聯優化,現代 JIT 編譯器已自動判斷,final 不再是內聯的必要條件。final 方法在子類中不能被隱藏(hide)。| 類型 | 初始化時機 | 規則 |
|---|---|---|
| 基本類型 | 聲明時 / 構造器 / 靜態塊 | 賦值後值不可改 |
| 引用類型 | 同上 | 引用地址不可改,但對象內容可變 |
| 空白 final (blank final) | 構造器中必須賦值 | 非靜態靠構造器,靜態靠靜態塊 |
| 接口常量 | 聲明時必須初始化 | 隱式 public static final |
| 局部變量 | 首次使用前 | 只需保證賦值一次即可 |
final List list = new ArrayList<>();
list.add("hello"); // ✅ 合法:修改對象內容
// list = new LinkedList<>(); // ❌ 編譯錯誤:嘗試改變引用
final String b = "hello";
String c = b + 2; // 編譯期優化為 "hello2"
String d = "hello";
String e = d + 2; // 運行期拼接
System.out.println("hello2" == c); // true
System.out.println("hello2" == e); // false
final 基本類型與字符串常量在編譯期被視為常量,直接進入常量池,影響 == 比較結果。
Java 要求匿名內部類捕獲的局部變量必須是 final 或「實際上最終」(effectively final)。本質是編譯器將變量值複製到內部類實例中,若變量可變會導致不一致。
// 技巧:用單元素數組繞過限制
final int[] counter = {0};
new Thread(() -> counter[0]++).start();
final 不僅是語法糖,更是設計意圖的聲明:
善用 final,讓代碼更安全、意圖更清晰、優化更充分。