static 是 Java 中用於修飾成員變量(靜態變量/類變量)、成員方法(靜態方法/類方法)、代碼塊(靜態初始化塊)的關鍵字。被 static 修飾的成員:
| 維度 | 靜態變量 static | 實例變量(無 static) |
|---|---|---|
| 稱呼 | 類變量 | 實例變量 |
| 調用方式 | 類名.變量名(推薦) / 實例.變量名 | 實例.變量名 |
| 存儲位置 | 方法區(JDK 8 之前永久代 / JDK 8+ 元空間)靜態區 | 堆內存,隨對象實例分配 |
| 生命週期 | 類加載 → 類卸載(隨類生命週期) | 對象創建 → 對象銷毀(GC 回收) |
| 副本數量 | 全局唯一,所有實例共享 | 每個實例一份,互不影響 |
| 適用場景 | 常量、共享配置、計數器、單例模式、緩存、工具類狀態 | 對象固有屬性(姓名、年齡、狀態等) |
public class Person {
private String name;
private int age;
// 所有中國人實例共享同一個 nationality 副本
private static String nationality = "中國";
// getter/setter 省略
}
修改 Person.nationality = "美國",所有已創建與未來創建的 Person 實例讀取到的均為「美國」。
類名.方法名())。this 引用不可用。public class StaticDemo {
private String name = "實例變量";
private static String address = "靜態變量";
public static void main(String[] args) {
// show1(); // 編譯錯誤:Cannot make a static reference to the non-static method show1()
show2(); // 正確
}
private void show1() { // 實例方法
System.out.println(name); // 訪問實例變量 OK
System.out.println(address); // 訪問靜態變量 OK
}
private static void show2() { // 靜態方法
// System.out.println(name); // 編譯錯誤:Cannot make a static reference to the non-static field name
System.out.println(address); // 訪問靜態變量 OK
}
}
public class MainDemo {
private static String address = "Beijing";
public static void main(String[] args) {
MainDemo.show(); // 類名調用,推薦
// new MainDemo().show(); // 實例調用,不推薦(編譯器警告)
}
private static void show() {
System.out.println(address);
}
}
static { ... } 塊,按聲明順序執行。DriverManager.registerDriver)、初始化單例。public class StaticBlockDemo {
private static String address = "china";
static {
System.out.println("first static block");
}
static {
System.out.println("second static block");
}
public static void main(String[] args) {
StaticBlockDemo.show(); // 觸發類初始化
}
private static void show() {
System.out.println(address);
}
}
/* 輸出:
first static block
second static block
china
*/
+-------------------------+
| Method Area | (JDK 8+: Metaspace, Native Memory)
| (Class Metadata) |
| +-------------------+ |
| | Static Variables | | <-- static 變量、常量、靜態方法字節碼
| | nationality="中國"| |
| | visitCount=0 | |
| +-------------------+ |
+-------------------------+
▲
│ 類加載器載入 Class 文件
│
+-------------------------+
| Heap Memory |
| +-------------------+ |
| | Person Instance 1 | | name="張三", age=20
| | nationality ─────┼──┼──┐
| +-------------------+ | │ 所有實例引用指向同一靜態區地址
| | Person Instance 2 | | │
| | nationality ─────┼──┘ │
| +-------------------+ │
| | Person Instance 3 | | │
| | nationality ───────────┘
| +-------------------+ |
+-------------------------+
public class Visitor {
int count = 0; // 實例變量
static int visitCount = 0; // 靜態變量
public Visitor() {
count++;
visitCount++;
}
}
public class StaticMemoryTest {
public static void main(String[] args) {
Visitor v1 = new Visitor();
System.out.println("v1.count=" + v1.count + ", visitCount=" + Visitor.visitCount); // 1, 1
Visitor v2 = new Visitor();
System.out.println("v2.count=" + v2.count + ", visitCount=" + Visitor.visitCount); // 1, 2
Visitor v3 = new Visitor();
v3.count = 5;
v3.visitCount = 0; // 修改靜態變量影響所有實例
System.out.println("v1.count=" + v1.count + ", v1.visitCount=" + v1.visitCount); // 1, 0
System.out.println("v2.count=" + v2.count + ", v2.visitCount=" + v2.visitCount); // 1, 0
System.out.println("v3.count=" + v3.count + ", v3.visitCount=" + v3.visitCount); // 5, 0
}
}
| 特性 | 說明 |
|---|---|
| 可修飾目標 | 成員變量、成員方法、代碼塊、內部類(靜態內部類) |
| 內存分配時機 | 類加載階段(準備階段分配默認值,初始化階段賦值與執行 static 塊) |
| 訪問方式 | 類名.成員(推薦),實例.成員(可行但不規範) |
| 共享性 | 全局唯一,所有實例、類自身共享 |
| 靜態方法限制 | 無 this/super;不可直接訪問非靜態成員;不可被覆寫(可隱藏) |
| 靜態代碼塊 | 類初始化執行一次,按順序,線程安全 |
| 繼承關係 | 子類不繼承父類靜態成員(但可繼承訪問權限);靜態方法不參與多態(隱藏而非覆寫) |
| 序列化 | static 修飾的變量不參與序列化(transient 效果) |
| 常見用途 | 常量(static final)、工具類方法、單例模式、計數器、緩存、配置共享、JDBC 驅動註冊 |
static final:命名全大寫下劃線分隔(MAX_SIZE),編譯期常量折疊優化。public final class StringUtils { private StringUtils(){} public static boolean isBlank(String s){...} }。static { } 中,確保類加載時自動完成。理解 static 的本質——「屬於類、共享全局、生命週期長」,是寫出高效、正確 Java 代碼的基石。合理使用靜態成員可簡化全局訪問、減少內存開銷;濫用則引入隱式耦合與併發風險。牢記「靜態只能訪問靜態、非靜態可訪問一切」,配合內存模型圖解,靈活應對面試與工程實戰。