final 和 static 是 Java 基礎中最核心、面試高頻、實踐易錯的兩大關鍵字。本文從內存模型、字節碼行為、JVM 初始化順序三個維度,徹底講透「引用不可變 vs 內容可變」、「靜態資源加載時機」、「static 修飾類/塊/導入」等核心考點。
public final class ImmutableUtils { ... } // 無法被 extends
public class Base {
public final void templateMethod() { ... } // 子類不可 override
}
public class FinalDemo {
public static void main(String[] args) {
final User u = new User("Alice");
u.setName("Bob"); // ✅ 編譯通過,運行正常
u = new User("Charlie"); // ❌ 編譯錯誤:cannot assign a value to final variable
System.out.println(u); // 輸出 User{name='Bob'}
}
}
class User {
private String name;
public User(String name) { this.name = name; }
public void setName(String name) { this.name = name; }
@Override public String toString() { return "User{name='" + name + "'}"; }
}
final 修飾引用類型變量時,鎖定的是「引用地址」,而非「對象內容」。對象內部狀態完全可以通過 setter 修改。
final int MAX = 100; // 值不可變,編譯期常量折疊
final double PI = 3.14159;
ClassName.field(推薦)或 instance.field(不推薦,IDE 警告)<clinit> 階段this,不能直接訪問非靜態成員Child.staticMethod() 調用子類版本public class StaticBlockDemo {
static int a = 10;
static int b;
static {
b = a + 5; // ✅ 可賦值
// System.out.println(c); // ❌ 編譯錯誤:非法前向引用
}
static int c = 20;
public static void main(String[] args) {
System.out.println(b); // 15
}
}
規則:按文本順序執行,可賦值後定義的靜態變量,但不能訪問(讀取)後定義的靜態變量。
public class Outer {
private static String config = "prod";
public static class Inner { // 只能修飾內部類
public void printConfig() {
System.out.println(config); // 可訪問外部類靜態成員
}
}
}
// 使用:無需 Outer 實例
new Outer.Inner().printConfig();
靜態內部類不持有外部類引用,是實現線程安全單例的標準模式:
public class Singleton {
private Singleton() {}
private static class Holder {
static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() { return Holder.INSTANCE; }
}
| 調用方 \ 被調用方 | 靜態變量/方法 | 實例變量/方法 |
|---|---|---|
| 靜態方法 | ✅ 直接訪問 | ❌ 編譯錯誤(需創建實例) |
| 實例方法 | ✅ 直接訪問 | ✅ 直接訪問 |
public class InitOrder {
// 1. 父類靜態變量/靜態塊(按文本順序)
// 2. 子類靜態變量/靜態塊(按文本順序)
// 3. 父類實例變量/實例塊
// 4. 父類構造器
// 5. 子類實例變量/實例塊
// 6. 子類構造器
}
class Parent {
static { System.out.println("Parent static"); }
{ System.out.println("Parent instance block"); }
Parent() { System.out.println("Parent constructor"); }
}
class Child extends Parent {
static { System.out.println("Child static"); }
{ System.out.println("Child instance block"); }
Child() { System.out.println("Child constructor"); }
}
public class Test {
public static void main(String[] args) {
new Child();
}
}
/* 輸出:
Parent static
Child static
Parent instance block
Parent constructor
Child instance block
Child constructor
*/
import static java.lang.Math.*; // 導入所有靜態成員
import static java.lang.System.out; // 導入單個
public class StaticImportDemo {
public static void main(String[] args) {
out.println(PI); // 直接用 PI,無需 Math.PI
out.println(sqrt(2)); // 直接用 sqrt
out.println(random());
}
}
慎用原則:僅用於極高頻、無歧義的常量/方法(如 Math.*、Assert.*),濫用會降低代碼可讀性。