Java 虛擬機將 Class 檔案載入記憶體,經驗證、轉換解析、初始化,形成可直接使用的 Java 型別。Java 動態擴展特性仰賴執行期動態載入與動態連結。類別從載入到卸載,生命週期包含七階段:
其中驗證、準備、解析統稱「連接」。前五階段啟動順序固定,但執行常交叉混合(如解析在某些情況延至初始化後,支援動態繫結)。
規範嚴格規定僅五種情況立即觸發初始化:
new、getstatic、putstatic、invokestatic 位元組碼指令,且類別未初始化。java.lang.reflect 反射呼叫類別。java.lang.invoke.MethodHandle 解析結果為 ref_getstatic 等方法句柄,且對應類別未初始化。除此之外皆為被動引用,不觸發初始化:
new SuperClass[10])。介面亦有初始化過程(編譯器產生 <clinit>()),但不要求父介面全部初始化,僅在真正使用父介面成員時才初始化。類別初始化則要求父類別全部先完成。
完成三件事:
java.lang.Class 物件,作為方法區資料存取入口。非陣列類別載入可由開發者控制(自訂類別載入器);陣列類別由 JVM 直接建立,元素型別最終仍靠類別載入器。
確保 Class 檔案位元組流符合 JVM 要求、不危害安全。四階段:
0xCAFEBABE、版本號、常量池支援度等,保證能正確解析存入方法區。StackMapTable 屬性描述基本區塊狀態,避免重複推導。正式為類別變數(static 欄位)配置記憶體並設初始值(通常為型別零值)。若欄位屬性表存在 ConstantValue 屬性(final static),則依指定值初始化。實例變數隨物件實例化在堆中配置。
將常量池內符號引用替換為直接引用。符號引用與記憶體佈局無關,同一符號引用多次解析常見(除 invokedynamic),JVM 可快取首次結果。invokedynamic 服務動態語言,不快取。
四類引用解析:類/介面、欄位、類方法、介面方法。
執行類別建構器 <clinit>() 方法(編譯器自動收集所有類別變數賦值與 static 區塊語句,按原始碼順序合成)。關鍵細節:
<clinit>() 收集順序依原始碼順序;static 區塊僅能存取定義在其之前的變數(可賦值不可讀取)。<clinit>() 執行前父類已完成。<clinit>()。<clinit>() 不需先執行父介面同方法。任意類別由「載入它的類別載入器」+「類別本身」共同確立唯一性。比較兩類別是否相等,前提為同一載入器載入。
%JAVA_HOME%\lib 或 -Xbootclasspath 指定路徑中被 JVM 識別的類別庫(如 rt.jar),不可被 Java 程式直接引用。sun.misc.Launcher$ExtClassLoader),載入 %JAVA_HOME%\lib\ext 或 java.ext.dirs 指定路徑類別庫,開發者可直接使用。sun.misc.Launcher$AppClassLoader),載入使用者類別路徑(classpath)類別庫,為 ClassLoader.getSystemClassLoader() 回傳值,無自訂載入器時為預設載入器。三者為父子關係,但採組合而非繼承實作。
ClassNotFoundException。優點:基礎類別由上層載入器載入,具備優先級層次,保障 Java 程式穩定運行。核心實作邏輯(loadClass):檢查已載入 → 父載入器委派 → 失敗則自身 findClass → 可選連結 → 回傳。
繼承 java.lang.ClassLoader 並覆寫 findClass() 即可。ClassLoader 職責:依類別名稱尋找/產生位元組碼,定義為 java.lang.Class 實例;另負責載入資源(圖片、配置檔等)。相關方法:loadClass()、findClass()、defineClass()、getResource() 等。