理解「實例成員」與「類別成員」的差異,是掌握 Java 物件導向核心觀念的關鍵。本文透過 AnIntegerNamedX 經典範例,詳細解析實例變數/方法與類別變數/方法的記憶體模型、存取規則、共享機制與常見錯誤。
| 特性 | 實例成員 | 類別成員 (static) |
|---|---|---|
| 宣告關鍵字 | 無 (預設) | static |
| 記憶體配置時機 | 每次 new 建立物件時 | 類別首次被載入/使用時 |
| 副本數量 | 每個物件擁有獨立副本 | 全類別共享單一副本 |
| 存取方式 | 透過物件參考 obj.field | 透過類別名 ClassName.field 或物件參考 |
| 方法可存取對象 | 實例變數、類別變數 | 僅類別變數 (除非建立物件) |
| 方法呼叫方式 | 需物件實例 | 可直接用類別名呼叫 |
class AnIntegerNamedX {
int x; // 實例變數
public int x() { // 實例方法
return x;
}
public void setX(int newX) { // 實例方法
x = newX;
}
}
行為驗證:
AnIntegerNamedX myX = new AnIntegerNamedX();
AnIntegerNamedX anotherX = new AnIntegerNamedX();
myX.setX(1);
anotherX.x = 2; // 直接存取 package-private 變數
// 輸出:
// myX.x = 1
// anotherX.x = 2
✅ 每個物件擁有獨立的 x 副本,互不干擾。
class AnIntegerNamedX {
static int x; // 類別變數
// 方法不變...
}
行為變化:
myX.setX(1);
anotherX.x = 2;
// 輸出:
// myX.x = 2
// anotherX.x = 2
⚠️ 所有實例共享同一個 x,任一處修改影響所有物件。
class AnIntegerNamedX {
int x; // 實例變數
static public int x() { // 類別方法
return x; // ❌ 編譯錯誤:Can't make static reference to nonstatic variable
}
static public void setX(int newX) { // 類別方法
x = newX; // ❌ 同上
}
}
錯誤原因:類別方法屬於類別,執行時沒有 this 參考,無法知道要存取哪個物件的實例變數。
class AnIntegerNamedX {
static int x; // 類別變數
static public int x() { // 類別方法
return x;
}
static public void setX(int newX) { // 類別方法
x = newX;
}
}
✅ 此時可直接用類別名存取:
AnIntegerNamedX.setX(1);
System.out.println(AnIntegerNamedX.x()); // 輸出 1
// 無需建立任何物件實例
實例方法內部存取 x 等同於 this.x,指向呼叫該方法的具體物件。這就是為何多個物件共享同一份方法程式碼卻能操作各自資料的原因。
範例中 x 為 package-private (無修飾詞),同套件可直接存取。實務上建議:
private + getter/setterpublic static finalprivate static + 靜態存取方法
Heap Memory Method Area (Class Metadata)
┌─────────────────┐ ┌──────────────────────┐
│ myX (ref) ──────┼──► ┌──────►│ AnIntegerNamedX.class│
└─────────────────┘ │ │ - static int x (1) │
│ │ - static methods │
┌─────────────────┐ │ └──────────────────────┘
│ anotherX (ref) ─┼──────┘
└─────────────────┘
│
▼
┌─────────────────┐
│ AnIntegerNamedX │ ◄── 每個實例有自己的 x (若非 static)
│ x = 1 │
└─────────────────┘
static int count 追蹤建立物件總數public static final double PI = 3.14159;private static Instance INSTANCE = new Instance();Math, Collections, Arrays 皆為全 static 方法實例成員屬於「物件」,類別成員屬於「類別」。static 關鍵字改變的是儲存位置、生命週期與存取方式。正確選擇:需要「每個物件有自己的狀態」用實例成員;需要「全域共享、不依賴具體物件」用類別成員。這是設計良好 Java 類別架構的基石。