核心定義
| 概念 | 關鍵字 | 特徵 | 內存行為 |
| 變量 | 無 / final (引用不可變) | 值可變,作用域內可重新賦值 | 棧內存 (基本類型) / 堆內存 (對象引用) |
| 常量 | final + static (類常量) / final (實例/局部常量) | 值不可變,編譯期或運行期確定 | 常量池 (字面量) / 堆/元空間 (對象引用) |
變量分類與作用域
public class VariableDemo {
// 1. 成員變量 (實例變量) - 堆內存,隨對象生命周期
private int instanceVar = 10;
private final int INSTANCE_CONST = 20; // 實例常量,構造器初始化
// 2. 類變量 (靜態變量) - 方法區/元空間,隨類生命周期
public static int classVar = 100;
public static final int CLASS_CONST = 200; // 類常量 (真正常量)
// 3. 局部變量 - 棧內存,隨方法調用生命周期
public void method(int param) { // 參數也是局部變量
int localVar = 1000; // 必須顯式初始化
final int LOCAL_CONST = 2000; // 局部常量
// localVar = 999; // OK
// LOCAL_CONST = 888; // 編譯錯誤
}
}
命名規範 (Alibaba Java 開發手冊標準)
| 類型 | 規範 | 正例 | 反例 |
| 類常量 | 全大寫,下劃線分隔 | MAX_RETRY_COUNT, DEFAULT_PAGE_SIZE | maxRetryCount, Max_Retry_Count |
| 實例/局部常量 | 同類常量,或按變量規範 (見下) | DEFAULT_TIMEOUT | defaultTimeout |
| 變量 (含參數) | lowerCamelCase,名詞/動詞短語 | userName, orderList, calculateTotal | UserName, user_name, temp, a1 |
| 布爾變量 | is/has/can/should 開頭 | isValid, hasPermission, canExecute | valid, permissionFlag |
| 集合變量 | 復數名詞或 List/Set/Map 後綴 | userList, idSet, nameMap | users, data, map |
final 關鍵字三大用法
- 修飾基本類型變量:值不可變 (編譯期常量折疊優化)
- 修飾引用類型變量:引用地址不可變,但對象內部狀態可變
final List list = new ArrayList<>();
list.add("a"); // OK
list = new ArrayList<>(); // 編譯錯誤
- 修飾方法/類:方法不可覆寫、類不可繼承
內存模型圖解 (JDK 8+)
┌─────────────────────────────────────┐
│ 堆 │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ Object A │ │ Object B │ │
│ │ final ref ────►│ (可變對象) │ │
│ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ 棧 (線程私有) │
│ main() frame │
│ int localVar = 10; │
│ final int CONST = 20; │
│ List ref ──────────────┐ │
└─────────────────────────────────────┘ │
▼
┌─────────────────────────────────────┐
│ 方法區 / 元空間 │
│ Class VariableDemo │
│ static final int CLASS_CONST=200 │ ◄── 常量池 (字面量)
│ static int classVar = 100 │
└─────────────────────────────────────┘
常見誤區與避坑指南
| 誤區 | 真相 |
| final 對象 = 不可變對象 | final 只鎖定引用,對象內部字段仍可修改。真正不可變需所有字段 final + 無 setter + 深度不可變 |
| static final 必須聲明時初始化 | 可在靜態代碼塊初始化,編譯器只要求構造器/靜態塊完成前賦值 |
| 局部變量有默認值 | 局部變量無默認值,使用前必須顯式賦值,否則編譯報錯 |
| 常量名全大寫就對了 | 僅限 static final 類常量;實例/局部 final 變量按變量命名規範 |
| final 能提升性能 | 基本類型/字符串常量會編譯期折疊,引用類型無性能增益,僅語義約束 |
最佳實踐清單
- 能 final 就 final:參數、局部變量、不可變字段,減少可變狀態,利於線程安全與 JIT 優化
- 常量集中管理:業務常量歸類放入
Constants.java 或按模塊劃分 OrderConstants, UserConstants
- 避免魔法數字:
if (status == 2) → if (status == OrderConstants.STATUS_PAID)
- 不可變對象優先:DTO/VO/實體盡量用
final 字段 + 構造器/Builder 模式,配合 Lombok @Value 或 @Data + @Accessors(fluent=true)
- 枚舉替代常量組:狀態碼、類型碼用
enum,天然單例、類型安全、可攜帶行為
總結
變量與常量是 Java 程序的「數據骨架」。理解 作用域、生命周期、內存區域、final 語義、命名規範,能寫出更健壯、可讀、可維護的代碼。記住:final 是引用鎖,不是對象鎖;常量是語義約束,非性能銀彈。