public class Producer {
Clerk clerk; // ← 這行是聲明,不是創建對象
// ...
clerk = new Clerk(); // ← 這行才是真正創建對象並賦值
}
| 操作 | 關鍵字 | 動作 | 內存影響 |
|---|---|---|---|
| 聲明 | 無 new | 在棧幀分配引用變量槽位,默認值 null | 極小(4/8 字節引用) |
| 實例化 | new | 堆中分配對象內存,調用構造器初始化,返回引用地址 | 對象大小(含對象頭、實例字段、對齊填充) |
| 賦值 | = | 將引用地址存入聲明的變量槽 | 無額外內存 |
棧 堆
┌──────────────┐ ┌──────────────────┐
│ Producer │ │ Clerk Object │
│ clerk ───────┼──────→│ header │
│ (引用) │ │ int id = 1 │
└──────────────┘ │ String name="A" │
└──────────────────┘
Clerk clerk = new Clerk(); 認為是一條語句 → 實際是聲明 + 實例化 + 賦值三步合寫,字節碼仍分三步執行。null 風險 → 聲明後未賦值直接調用方法拋 NullPointerException。Clerk clerk = new Clerk(); 減少 null 窗口期。final Clerk clerk = new Clerk(); 防止意外重新賦值,編譯器優化更好。Clerk,而非內部 new,便於測試與解耦。Clerk clerk; 只是告訴編譯器「我要一個名為 clerk、類型為 Clerk 的遙控器」,new Clerk() 才是「造出一台 Clerk 機器並把遙控器指向它」。分清聲明與實例化,是理解 Java 內存模型、避免空指針異常的基石。