static 是 Java 中修飾成員變量、方法、代碼塊、內部類的關鍵字,標誌著「屬於類而非實例」。理解其內存佈局與語義規則是寫出高效、正確 Java 代碼的基石。
ClassName.staticMember)| 維度 | 靜態變量 (static field) | 實例變量 |
|---|---|---|
| 存儲區域 | 方法區 → 元空間 (Class 對象內部) | 堆內存 (對象實例內部) |
| 加載時機 | 類加載階段 (準備階段分配內存、賦零值;初始化階段賦指定值) | 對象實例化時 (new / 反射 / 反序列化) |
| 生命週期 | 類加載 → 類卸載 (極長,通常伴隨應用生命週期) | 對象創建 → GC 回收 (較短) |
| 默認值 | 準備階段賦零值 (0/false/null) | 對象分配時賦零值 |
| 線程可見性 | 需 volatile 或同步保證可見性 | 需 volatile 或同步保證可見性 |
| 調用者 \ 被調用 | 靜態成員 | 非靜態成員 |
|---|---|---|
| 靜態方法 | ✓ 直接訪問 | ✗ 編譯錯誤 (無 this 引用) |
| 非靜態方法 | ✓ 直接訪問 | ✓ 通過 this 訪問 |
核心原因:靜態方法屬於類,加載時無實例存在,上下文中無 this/super 引用,故無法訪問實例成員。
public class StaticDemo {
static { System.out.println("1. 父類靜態塊"); } // 1
static int a = 10; // 2
static { System.out.println("2. 父類靜態塊2"); } // 3
{ System.out.println("3. 父類實例塊"); } // 5
int b = 20; // 6
public StaticDemo() { System.out.println("4. 父類構造器"); } // 7
}
// 子類繼承順序:
// 1. 父類靜態變量/靜態塊 (按文本順序)
// 2. 子類靜態變量/靜態塊 (按文本順序)
// 3. 父類實例變量/實例塊 (按文本順序)
// 4. 父類構造器
// 5. 子類實例變量/實例塊 (按文本順序)
// 6. 子類構造器
關鍵點:靜態初始化 僅在類首次主動使用時執行一次 (new、靜態調用、反射、主類啟動)。
// ❌ 危險:併發環境下可能拋 NPE 或數據不一致
public static List LIST = new ArrayList<>();
// ✅ 推薦:靜態工廠 + Collections.unmodifiableList
public static final List IMMUTABLE_LIST = Collections.unmodifiableList(
Arrays.asList("a", "b", "c"));
// ✅ 可變併發安全:CopyOnWriteArrayList / ConcurrentHashMap
public static final List CONCURRENT_LIST = new CopyOnWriteArrayList<>();
import static java.lang.Math.PI;
import static java.util.Collections.emptyList;
// 使用 PI, emptyList() 無需類名前綴
public class Singleton {
private Singleton() {}
private static class Holder {
static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return Holder.INSTANCE;
}
}
類加載生命週期: 加載 → 驗證 → 準備(靜態變量分配內存+零值) → 解析 → 初始化(靜態變量賦值+靜態塊) → 使用 → 卸載 靜態變量初始化時機: 準備階段: static int a; → a = 0 (零值) 初始化階段: static int a = 10; → a = 10 (指定值) + 靜態塊執行
設計原則:
static適合「工具方法、常量、無狀態服務、單例承載、類級別配置」。業務狀態、請求上下文、可變集合嚴禁 static。善用final修飾靜態引用防止重賦值,配合不可變對象實現線程安全共享。