static 修飾符將成員從「實例級」提升為「類級」,改變內存佈局、初始化時機與訪問方式。本文通過對比實驗揭示靜態與實例成員的本質差異,總結使用原則與編程陷阱。
public class User {
private String name; // 實例字段
private static int count; // 靜態字段
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public static int getCount() { return count; }
public static void main(String[] args) {
User u1 = new User(); u1.setName("Rose");
User u2 = new User(); u2.setName("Kate");
u1.count = 10;
u2.count = 20;
System.out.println("u1.name=" + u1.getName() + " u2.name=" + u2.getName());
System.out.println("u1.count=" + User.getCount() + " u2.count=" + u2.count);
}
}
輸出:
u1.name=Rose u2.name=Kate u1.count=20 u2.count=20
| 維度 | 實例成員 | 靜態成員 |
|---|---|---|
| 所屬 | 對象實例 | 類本身 |
| 內存位置 | 堆中對象內部 | 方法區(元空間)類元數據 |
| 初始化時機 | 每次 new 時 | 類加載時僅一次 |
| 共享性 | 各實例獨立副本 | 所有實例共享單份 |
| 訪問方式 | obj.field / obj.method() | Class.field / Class.method()(亦可對象訪問但不推薦) |
| 方法內可直接訪問 | 實例/靜態皆可 | 僅能直接訪問靜態,訪問實例需先 new Obj() |
public static void test() {
setName("test"); // 編譯錯誤:非靜態方法不能從靜態上下文中引用
}
修正:
public static void test() {
new User().setName("test"); // 創建實例後再調用
}
staticstatic 會導致併發安全問題、測試困難、內存洩漏(靜態集合持有大對象)AtomicInteger、synchronized)static 字段不參與序列化「靜態屬於類,一份共享全體; 實例屬對象,各自獨立存儲。 靜態方法無 this,實例字段不可直接取; 濫用靜態害深遠,設計之初要謹慎。」