一覽表:核心差異
| 維度 | static (靜態) | final (最終) |
| 核心語義 | 屬於類,不屬於實例 | 不可變、不可繼承、不可重寫 |
| 內存位置 | 方法區(元空間),類加載時分配 | 變量:編譯期常量進常量池,運行期賦值在堆/棧 |
| 生命周期 | 隨類加載→卸載 | final 變量:引用不可變(對象內部狀態仍可變) |
| 繼承關係 | 靜態方法隱藏,非多態 | final 方法禁止重寫,final 類禁止繼承 |
1. 修飾變量(字段)
static 變量(類變量)
public class Counter {
private static int count = 0; // 所有實例共享
public Counter() { count++; }
public static int getCount() { return count; }
}
- 共享:所有實例、靜態方法均訪問同一份內存。
- 訪問:
Counter.count 或 obj.count(不推薦實例訪問,易誤導)。
- 初始化:默認值(0/null/false),或靜態塊/聲明處賦值。
final 變量(常量/不可變引用)
public class Config {
// 編譯期常量:public static final + 基本類型/String + 聲明處賦值
public static final int MAX_SIZE = 100;
public static final String NAME = "App";
// 運行期常量:構造器/實例塊賦值,每個實例可不同
public final long createTime;
public Config() { this.createTime = System.currentTimeMillis(); }
}
- 基本類型:值不可變。
- 引用類型:引用不可變(不能指向新對象),但對象內部狀態可變(如
final List list = new ArrayList(); list.add(1); // 合法)。
- 編譯期常量(Constant Variable)會被編譯器內聯到調用處,修改後需重新編譯調用方。
static final 組合:全局常量
public class Constants {
public static final double PI = 3.1415926;
public static final String VERSION = "1.0.0";
}
- 類加載時初始化,全程不可變,線程安全,編譯器內聯優化。
- 命名慣例:全大寫下劃線分隔。
2. 修飾方法
static 方法
- 屬於類,
ClassName.method() 調用。
- 不可訪問非靜態成員(無 this)。
- 不可被重寫:子類同簽名方法為隱藏,調用靜態綁定(編譯期確定),非多態。
- 工具類、工廠方法、單例 getInstance 典型場景。
final 方法
- 禁止子類重寫,保證行為不被篡改。
- 編譯器可內聯優化(現代 JVM 熱點編譯已自動內聯,final 非必需)。
- 私有方法隱式 final。
3. 修飾類
final class:不可被繼承(如 String, Integer, LocalDate)。
abstract final:非法,互斥。
- 所有方法隱式 final。
4. 修飾代碼塊
public class BlockDemo {
static { System.out.println("靜態塊:類加載執行一次"); }
{ System.out.println("實例塊:每次 new 執行,構造器前"); }
}
static { }:類初始化階段執行,僅一次,用於靜態字段複雜初始化。
- 無
final { } 語法(final 不能修飾塊)。
5. 修飾局部變量與參數
public void process(final int param, final List list) {
// param = 10; // 編譯錯誤
// list = new ArrayList<>(); // 編譯錯誤
list.add("allowed"); // 合法:修改對象內部狀態
final int local = compute(); // 僅賦值一次
// local = 20; // 錯誤
}
- final 參數:防止方法內誤重賦值,語義清晰。
- 匿名內部類/Lambda 捕獲的局部變量隱性要求 final(或 effectively final)。
常見誤區與避坑
| 誤區 | 正確理解 |
| final 對象不可變 | 引用不可變,內部狀態可變。真正不可變需類設計(如 String、包裝類、Guava ImmutableList)。 |
| static 方法不能調用非靜態 | 可通過「實例引用」調用非靜態,但無 this 隱式引用。 |
| static final 基本類型必須聲明處賦值 | 可在靜態塊賦值,但失去編譯期常量內聯優化。 |
| final 方法性能更好 | 現代 JVM 熱點編譯自動內聯熱點方法,final 非前提。 |
| 枚舉就是 final class | 枚舉更強:防反射、防序列化破壞、天然單例。 |
最佳實踐清單
- 工具類構造器
private,方法 static,類 final。
- 不可變對象:所有字段
private final,無 setter,構造器完全初始化,防御性拷貝可變組件。
- 常量:
public static final + 基本類型/String,大寫命名。
- 參數、局部變量、捕獲變量:默認加
final,減少誤賦值,利於 JIT 優化。
- 繼承體系設計:基類方法默認
final,確需多態再開放;類默認 final,確需繼承再開放(Effective Java Item 19)。