靜態成員屬於類,非靜態成員屬於實例。靜態上下文無法直接訪問非靜態成員;非靜態上下文可自由訪問靜態成員。
靜態方法/字段在類加載階段就已分配內存,無需實例化;非靜態成員依賴對象實例存在。
public class StaticAccessDemo {
static String staticField = "靜態字段";
String instanceField = "實例字段";
static void staticMethod() {
System.out.println(staticField); // ✓ 靜態訪問靜態
// System.out.println(instanceField); // ✗ 編譯錯誤:非靜態字段不能從靜態上下文引用
// instanceMethod(); // ✗ 編譯錯誤:非靜態方法不能從靜態上下文引用
}
void instanceMethod() {
System.out.println(staticField); // ✓ 非靜態訪問靜態
System.out.println(instanceField); // ✓ 非靜態訪問非靜態
staticMethod(); // ✓ 非靜態調用靜態方法
}
public static void main(String[] args) {
StaticAccessDemo obj = new StaticAccessDemo();
obj.instanceMethod(); // 實例調用非靜態
StaticAccessDemo.staticMethod(); // 類調用靜態
}
}
| 錯誤代碼 | 原因 | 修正 |
|---|---|---|
| Cannot make a static reference to the non-static field | 靜態方法引用實例字段 | 改為實例方法,或將字段改為 static |
| Cannot make a static reference to the non-static method | 靜態方法調用實例方法 | 創建對象後調用,或將方法改為 static |
class InitOrder {
static { System.out.println("1. 靜態塊"); }
{ System.out.println("2. 實例塊"); }
public InitOrder() { System.out.println("3. 構造器"); }
static { System.out.println("4. 靜態塊2"); }
}
// 輸出:1 → 4 → 2 → 3(類加載時執行靜態塊,實例化時執行實例塊+構造器)
Collections, StringUtils)public static final 命名全大寫下劃線static AtomicInteger / static ConcurrentHashMapinvokestatic 字節碼指令直接調用靜態方法,無需對象引用;invokevirtual 需要對象引用調用實例方法。這就是為什麼靜態方法綁定在編譯期(靜態綁定),實例方法綁定在運行期(動態分派)的底層原因。
掌握 static 訪問規則,本質是理解類加載生命週期與內存分配區域的對應關係。遇到編譯報錯時,先問自己:「這裡有實例嗎?」