JVM 四大子系統:
.class 文件 → 內存《JVM Specification》定義:
ClassFile {
u4 magic; // 魔數 0xCAFEBABE
u2 minor_version; // 副版本號
u2 major_version; // 主版本號
u2 constant_pool_count; // 常量池計數
cp_info constant_pool[]; // 常量池
u2 access_flags; // 訪問標志
u2 this_class; // 當前類索引
u2 super_class; // 父類索引
u2 interfaces_count; // 接口計數
u2 interfaces[]; // 接口索引表
u2 fields_count; // 字段計數
field_info fields[]; // 字段表
u2 methods_count; // 方法計數
method_info methods[]; // 方法表
u2 attributes_count; // 屬性計數
attribute_info attributes[]; // 屬性表
}
Class Loader 只負責加載符合結構的文件,能否運行由 Execution Engine 決定。
| 方式 | 觸發場景 | 本質 |
|---|---|---|
| 隱式加載 | new、getstatic、putstatic、invokestatic 等字節碼指令 | JVM 自動觸發 |
| 顯式加載 | Class.forName()、ClassLoader.loadClass() | 代碼主動調用 |
本質相同:最終都委派給 ClassLoader 載入。區別在於觸發時機與調用鏈。
Bootstrap ClassLoader (C++ 實現,無 Java 引用)
↑ 父子關系
ExtClassLoader (加載 jre/lib/ext/*.jar)
↑
AppClassLoader (加載 classpath 下類,即用戶代碼)
ClassNotFoundExceptionjava.lang.String)ClassLoader loader = String.class.getClassLoader(); // Bootstrap → null
ClassLoader extLoader = loader.getParent(); // ExtClassLoader
ClassLoader appLoader = extLoader.getParent(); // AppClassLoader
// 注意:Bootstrap 由 C++ 實現,getClassLoader() 返回 null
加載 → 驗證 → 準備 → 解析 → 初始化 → 使用 → 卸載
java.lang.Class 對象作為訪問入口<clinit>() 方法:靜態變量賦值語句、靜態代碼塊、父類初始化<clinit>() 同步執行new、getstatic、putstatic、invokestatic 字節碼指令(訪問靜態成員)java.lang.reflect 反射調用main())MethodHandle 解析 REF_getStatic/REF_putStatic/REF_invokeStaticclass A { static { sysout("Static A"); } { sysout("Instance A"); } A() { sysout("Constructor A"); } }
class B extends A { static { sysout("Static B"); } { sysout("Instance B"); } B() { sysout("Constructor B"); } }
class C extends B { static { sysout("Static C"); } { sysout("Instance C"); } C() { sysout("Constructor C"); } }
public class Main {
static { sysout("Static Main"); }
{ sysout("Instance Main"); }
Main() { sysout("Constructor Main"); }
public static void main(String[] args) { new C(); }
}
Static Main
Static A
Static B
Static C
Instance A
Constructor A
Instance B
Constructor B
Instance C
Constructor C
javap -c C.class 顯示構造函數:
C();
Code:
0: aload_0
1: invokespecial #1 // Method B."":()V ← 先調用父構造器
4: getstatic #2 // Field System.out
7: ldc #3 // String "Instance C."
9: invokevirtual #4 // println
12: getstatic #2
15: ldc #5 // String "Constructor C."
17: invokevirtual #4
20: return
結論:非靜態初始化塊 被編譯器插入到 構造函數首行(父構造器調用之後,自身構造器代碼之前)。
new C(); // 首次觸發所有靜態初始化
new B(); // 僅觸發 B/A 的實例初始化,靜態不再執行
輸出:
Static Main
Static A
Static B
Static C
Instance A
Constructor A
Instance B
Constructor B
Instance C
Constructor C
Instance A
Constructor A
Instance B
Constructor B
靜態初始化塊每類只執行一次(類加載時),實例初始化塊每次 new 都執行。
面試必考點:雙親委派破壞場景、類加載器隔離應用、初始化順序、熱插拔原理。