核心區別
| 維度 | 類變量 (static) | 實例變量 |
| 關鍵字 | static | 無 |
| 所屬 | 類 | 對象實例 |
| 內存位置 | 方法區 | 堆 |
| 生命週期 | 類加載 ~ 類卸載 | 對象創建 ~ GC 回收 |
| 共享性 | 所有實例共享一份 | 每個實例獨立一份 |
| 訪問方式 | 類名.變量 / 實例.變量 | 只能 實例.變量 |
代碼演示
public class Demo {
static int classVar = 0; // 類變量
int instanceVar = 0; // 實例變量
public static void main(String[] args) {
Demo d1 = new Demo();
Demo d2 = new Demo();
d1.classVar = 10; // 等同 Demo.classVar = 10
d1.instanceVar = 20;
System.out.println(d2.classVar); // 10 (共享)
System.out.println(d2.instanceVar); // 0 (獨立)
}
}
內存佈局圖解
方法區
+------------------+
| Demo.class | ← 包含 classVar = 10
+------------------+
堆
+------------------+ +------------------+
| d1 實例 | | d2 實例 |
| instanceVar = 20 | | instanceVar = 0 |
+------------------+ +------------------+
常見誤區與最佳實務
- 誤以為 static 修飾對象就不可變:static 只固定引用,對象內部狀態仍可修改。
正確:static final List LIST = new ArrayList<>(); 引用不可變,但列表內容可變。
- 靜態方法中不可直接訪問非靜態成員:因無 this 引用。需先創建實例。
- 常量定義:
static final 大寫下劃線,編譯期常量內聯,修改需重新編譯調用方。
- 單例模式:利用類變量持有唯一實例,配合私有構造器。
進階:static 代碼塊與初始化順序
public class InitOrder {
static { System.out.println("1. 靜態代碼塊"); }
static int sv = 10;
{ System.out.println("3. 實例代碼塊"); }
int iv = 20;
public InitOrder() { System.out.println("4. 構造器"); }
public static void main(String[] args) {
System.out.println("2. main 開始");
new InitOrder();
}
}
// 輸出順序:1 → 2 → 3 → 4