變量與常量是程序狀態管理的核心。本文系統講解 Java 變量聲明、初始化、作用域規則,深度剖析 final 修飾變量、方法、類的語義差異,並結合 JVM 記憶體模型解讀常量池、引用不可變與對象可變的本質區別。
// 聲明
int x; // 未初始化(成員變量有預設值,局部變量無)
int y = 10; // 聲明並初始化
// 多變量聲明
int a = 1, b = 2, c = 3;
強類型規則:賦值類型必須兼容,int x = 3.14 編譯報錯(需強轉)。
final double PI = 3.14159;
final int MAX_SIZE = 100, MIN_SIZE = 0; // 多常量鏈式聲明
// 先聲明後賦值(僅限一次)
final int CONFIG_VAL;
CONFIG_VAL = 42; // 僅能賦值一次
命名慣例:全大寫 + 下劃線分隔(MAX_CONNECTIONS)。
| 修飾目標 | 語義 | 典型場景 |
|---|---|---|
| 變量 | 引用/值不可變更(引用不可指向新對象,基本型別值不可改) | 常量定義、不可變對象引用、方法參數防修改 |
| 方法 | 不可被子類重寫(可被繼承、重載) | 模板方法模式固定骨架、安全敏感操作、效能優化(內聯) |
| 類 | 不可被繼承,所有方法隱含 final | String、Integer 等不可變類、工具類、安全類 |
| 修飾組合 | 初始化位置 | 說明 |
|---|---|---|
final 實例變量 | 聲明處 / 實例初始化塊 / 所有構造器 | 對象創建前必須初始化 |
static final 類常量 | 聲明處 / 靜態初始化塊 | 類加載階段初始化 |
final 局部變量 | 聲明處 / 首次賦值前 | 編譯器流分析保證確定性賦值 |
final List list = new ArrayList<>();
list.add("A"); // ✅ 合法:修改對象內容
list = new ArrayList<>(); // ❌ 編譯錯誤:改變引用指向
// 真正不可變需配合不可變集合
final List immutable = Collections.unmodifiableList(list);
本質:final 保護的是「引用這把槍的指向」,而非「槍裡的子彈」。
interface Config {
int MAX = 100; // 隱含 public static final
String NAME = "App"; // 等同 public static final String NAME = "App";
}
設計哲學:介面定義「契約」,常量屬於契約一部分,天然不可變,保證所有實現類共享一致語義。
void execute(Runnable task) { new Thread(task).start(); }
void demo() {
final int count = 10; // JDK 8+ 可省略 final,但必須「實際上 final」
execute(() -> System.out.println("Count: " + count));
}
原因:匿名類實例可能比方法執行壽命長,需複製變量值到實例中,final 保證值不變,避免數據競爭。
final,需修改時再移除(減少可變狀態)final 防止誤賦值final 類final 避免意外重賦值導致 GC 延遲