final 是 Java 中用於限制可變性的關鍵字,可修飾類、方法、變數(含欄位、參數、區域變數)。本文系統梳理其語義、編譯期行為、宏替換機制與常見誤區,助你徹底掌握。
public final class ImmutableClass {
// 無法被 extends
}
final。String、Integer、LocalDate 等不可變類。public class Parent {
public final void doSomething() { ... }
// 可重載(參數不同),不可重寫
public final void doSomething(int x) { ... }
}
final 方法,但可重載。private 方法不可被子類存取,自然也就無法重寫。若父類有 private final void foo(),子類定義同名方法屬「新定義」而非重寫,不衝突。final 方法進行內聯優化(JIT 亦會)。| 型別 | final 效果 | 範例 |
|---|---|---|
| 基本型別 (int, double...) | 值不可變 | final int MAX = 100; |
| 參考型別 (Object, List...) | 引用不可變(指向同一物件),但物件內部狀態可變 | final List<String> list = new ArrayList<>(); list.add("ok"); // 合法 |
public class FinalFieldDemo {
// 靜態常數:編譯期常量(constant variable)
static final int COMPILE_CONST = 42;
// 實例常數:運行期常量
final int instanceConst;
// 參考型別 final
final List<String> items = new ArrayList<>();
public FinalFieldDemo(int v) {
this.instanceConst = v; // 必須在建構子賦值
}
}
當變數滿足:static final + 基本型別或 String + 編譯期常量表達式賦值 → 成為 編譯期常量,編譯器會將其直接內聯到位元碼中。
public class ConstantFolding {
final String a = "Hello"; // 非 static,非編譯期常量
static final String b = "World"; // 編譯期常量
static final String c = b + "!"; // 編譯期常量(常量表達式)
void test() {
String s1 = a + b; // 運行期拼接:new StringBuilder().append(a).append(b)
String s2 = "Hello" + "World"; // 編譯期直接成 "HelloWorld"
String s3 = b + "!"; // 編譯期直接成 "World!"
}
}
實務啟示:大量字串拼接的常量建議用 static final 定義,減少運行期 StringBuilder 開銷。
final List<String> list = new ArrayList<>();
list.add("A"); // ✅ 合法:修改物件內部狀態
// list = new ArrayList<>(); // ❌ 編譯錯誤:改變引用
對策:需真正不可變集合請用 Collections.unmodifiableList()、Guava ImmutableList 或 Java 9+ List.of()。
JIT 編譯器根據熱點分析決定是否內聯,final 僅是提示。過度依賴 final 達到效能優化屬早優化。
final,減少可變狀態,提升可讀性與執行緒安全。final 欄位 + 無 setter,構建不可變物件。UPPER_SNAKE_CASE(如 MAX_RETRY_COUNT)。public static final,建議省略修飾符保持簡潔。static final 卻在靜態塊中複雜初始化,易導致類載入死鎖或異常。final 是 Java 表達「不可變性意圖」的核心工具:
static final + 常量表達式 → 位元碼內聯,提升效能善用 final,能讓代碼更健壯、意圖更清晰、並發更安全。