static 與 final 是 Java 中最基礎也最核心的修飾符,它們分別掌管著「類級別共享」與「不可變性」兩大語義。本文將從記憶體佈局、語法規則、設計意圖三個層面,徹底講透這兩個關鍵字的正確用法與常見誤區。
ClassName.staticField;雖可用 obj.staticField 但會產生編譯警告。this 引用,不能訪問實例變量/方法,只能訪問靜態成員。Math.max())、工廠方法、單例模式 getInstance()。main() 之所以是 static,是因為程式啟動時尚無任何物件,JVM 需直接以類名調用入口。static {
// 類加載時執行一次,用於複雜靜態變量初始化
// 如載入驅動、初始化連接池配置等
}
一個類可有多個 static 塊,按聲明順序依次執行。
import static java.lang.System.out;
import static java.lang.Math.*;
out.println("PI = " + PI); // 直接用 out、PI
簡化常用靜態成員調用,濫用會降低可讀性,建議僅用於極高頻工具方法/常量。
String.hashCode())。private、static 方法隱含 final。String、Integer、Math)。早期建議「所有方法預設 final」以助內聯,但現代 JVM(HotSpot)擁有強大的類層次分析(CHA)與內聯緩存:若發現某方法在運行期從未被覆寫,JIT 會自動將其內聯;若後續載入子類覆寫,JIT 會執行去優化並重新編譯。因此,無需盲目加 final,專注於領域語義表達即可。
| 代碼片段 | 結果 | 原因 |
|---|---|---|
final List list = new ArrayList(); list.add(1); | 合法 | 引用不變,物件內容可變 |
final int i; i = 1; i = 2; | 編譯錯誤 | 空白 final 只能賦值一次 |
static void test() { this.x = 1; } | 編譯錯誤 | static 方法無 this |
class A { static void m() {} } class B extends A { void m() {} } | 編譯錯誤 | 靜態方法不能被非靜態覆寫(隱藏而非覆寫) |
深入理解 static 與 final,不僅能寫出正確代碼,更能從 JVM 視角審視設計決策,這正是從「會用」進階到「善用」的關鍵一步。