核心區別對比表
| 維度 | 實例變量 (非 static) | 靜態變量 (static) |
| 歸屬 |
屬於**對象實例** (每個 new 出來的對象擁有獨立副本) |
屬於**類** (全進程僅一份,類加載時分配) |
| 內存區域 |
堆內存 (對象實例內部) |
方法區 (JDK8: Metaspace,JDK7: PermGen) / 堆 (類對象) |
| 生命週期 |
隨對象創建而分配,隨對象 GC 而銷毀 |
類加載 (準備階段) 分配,類卸載 (極少) 釋放,伴隨 JVM 生命週期 |
| 訪問方式 |
`obj.field` / `obj.method()` (需實例引用) |
`ClassName.field` / `ClassName.method()` (推薦);也可 `obj.field` 但會警告 |
| 默認值 |
數值 0、布爾 false、引用 null (準備階段賦零值,初始化階段賦寫代碼值) |
同實例變量,但僅在類加載準備階段賦一次零值 |
| 線程安全 |
各線程操作各自對象副本,**天然隔離** (除非共享同一對象引用) |
**全局共享**,多線程並發讀寫**必須同步** (volatile/synchronized/Atomic*/Lock) |
| 序列化 |
默認序列化 |
`transient` / `static` 修飾的**不參與序列化** |
代碼演示:賦值、訪問、修改
public class VariableDemo {
// 實例變量
private String instanceName = "默認實例值";
// 靜態變量
private static String staticName = "默認靜態值";
private static int counter = 0;
// 實例方法訪問實例/靜態變量
public void printInstance() {
System.out.println("實例: " + instanceName + ", 靜態: " + staticName);
}
// 靜態方法僅能直接訪問靜態成員
public static void printStatic() {
System.out.println("靜態上下文: " + staticName + ", 計數: " + counter);
// System.out.println(instanceName); // 編譯錯誤:非靜態上下文
}
// 修改實例變量
public void setInstanceName(String name) {
this.instanceName = name; // this 可省略
}
// 修改靜態變量 (線程不安全示例)
public static void incrementUnsafe() {
counter++; // 非原子:讀-改-寫
}
// 線程安全修改
private static final AtomicInteger safeCounter = new AtomicInteger(0);
public static void incrementSafe() {
safeCounter.incrementAndGet();
}
public static void main(String[] args) {
// 類加載時靜態變量已就緒
VariableDemo.printStatic(); // 輸出默認值
// 創建實例
VariableDemo obj1 = new VariableDemo();
VariableDemo obj2 = new VariableDemo();
obj1.setInstanceName("對象1");
obj2.setInstanceName("對象2");
obj1.printInstance(); // 實例: 對象1, 靜態: 默認靜態值
obj2.printInstance(); // 實例: 對象2, 靜態: 默認靜態值 (共享)
// 通過類名修改靜態變量
VariableDemo.staticName = "修改後靜態值";
obj1.printInstance(); // 實例: 對象1, 靜態: 修改後靜態值 (obj2 也受影響)
}
}
常見誤區與避坑
- 靜態方法中調用非靜態成員:編譯錯誤。靜態上下文無 `this` 引用。
- 以為 `obj.staticField` 創建副本:仍訪問同一靜態變量,編譯器會警告「靜態成員應通過類名訪問」。
- 多線程環境直接 `static int count++`**:計數丟失。必須用 `AtomicInteger`、`LongAdder` (高併發)、`sync` 或 `Lock`。
- 常量定義缺少 `final`**:`static String CONFIG = "v1";` 可被任意修改 → `static final String CONFIG = "v1";` (編譯期常量折疊,不可變)。
- 單例模式雙重檢查鎖缺少 `volatile`**:指令重排導致部分初始化對象可見 → `private static volatile Singleton instance;`。
最佳實踐清單
- **默認用實例變量**;僅在「全局唯一狀態/常量/工具方法/緩存/計數器」場景用 `static`。
- **靜態變量私有化** (`private static`),提供靜態 `getter/setter` 封裝同步邏輯。
- **常量用 `static final`** 命名 `UPPER_SNAKE_CASE`,基本類型/字符串編譯期內聯。
- **線程安全首選無鎖工具** (`Atomic*`、`LongAdder`、`ConcurrentHashMap`),避免 `synchronized` 性能開銷。
- **避免可變靜態集合** (`static List list = new ArrayList<>();`) → 改用 `Collections.synchronizedList` 或 `CopyOnWriteArrayList` 或線程局部變量 `ThreadLocal`。