?
?
一、加載(Loading)
目標:把字節碼文件(.class)“讀入 JVM”,生成類的 “半成品”(Class
?對象)。
?
- Bootstrap ClassLoader(啟動類加載器):
- 負責加載 JVM 核心類庫(如?
java.lang
?包),用 C++ 實現(不同 JVM 有差異),無對應的 Java 類。
- 負責加載 JVM 核心類庫(如?
- Platform ClassLoader(平臺類加載器):
- 加載 Java 標準庫擴展(如?
java.sql
、javax
?包),JDK 9 后從?Extension ClassLoader
?改名而來。
- 加載 Java 標準庫擴展(如?
- App ClassLoader(應用類加載器):??
- 加載項目自己寫的類、第三方庫(classpath 路徑下的類),是開發中最常用的加載器。
?
類加載器(不同 JDK 版本的差異)
- 低版本 JDK
- 啟動類加載器:負責加載
rt.jar
(包含 Java 核心類庫)中的類。 - 擴展類加載器:負責加載
ext
目錄下的擴展類庫中的類。 - 系統類加載器(
System ClassLoader
):負責加載classpath
中程序員自己編寫的類。
- 啟動類加載器:負責加載
- 高版本 JDK(9+,引入模塊化思想)
- 啟動類加載器:仍負責加載 JDK 核心類庫。
- 平臺類加載器:替代低版本的擴展類加載器,負責加載 Java 平臺擴展的非核心類。
- 應用類加載器:替代低版本的系統類加載器,負責加載
classpath
中的類。 - 變化:
rt.jar
和ext
目錄消失,類庫被拆分為多個模塊(.jmod
文件),不同模塊由不同類加載器加載。
?
流程:類加載器按?雙親委派機制?工作(優先讓父加載器嘗試加載,保證核心類不被篡改),最終找到字節碼文件,讀入內存并生成?Class
?對象,存入方法區。
二、鏈接(Linking)
目標:把 “半成品類” 變成可執行的 “成品”,拆成?驗證、準備、解析?三步:
1. 驗證(Verify)
- 檢查字節碼是否符合 JVM 規范(比如魔數是否是?
0xCAFEBABE
、語法是否合法),防止惡意 / 錯誤字節碼搞崩 JVM。
2. 準備(Prepare)
- 給類的靜態變量分配內存 + 設置默認值(比如?
static int num = 10
,準備階段會先設?num = 0
,真正賦值在初始化階段)。 - 注意:靜態常量(
static final
)直接賦 “用戶寫的值”(比如?static final int num = 10
,準備階段就會設?num = 10
?)。
3. 解析(Resolve)
- 把符號引用替換成直接引用:比如代碼里寫?
Object obj = new Object()
,編譯后是 “符號引用”(類似 “找名為?Object
?的類”),解析階段會換成內存地址(直接引用),讓 JVM 真正能找到對應的類。
三、初始化(Initialization)
目標:執行類的靜態代碼塊、給靜態變量賦 “用戶寫的值”(比如?static int num = 10
?在這里真正賦值為?10
?)。
?
- 觸發時機:首次用類的靜態成員、創建對象、反射調用等(遵循?主動使用規則?)。
- 流程:按代碼順序執行靜態變量賦值、靜態代碼塊,完成后類才算真正 “可用”。
關鍵總結
- 類加載器:用 “雙親委派” 保證類加載安全,避免核心類被篡改。
- 鏈接階段:驗證字節碼合法性 → 給靜態變量分配內存 → 把符號引用轉成內存地址。
- 初始化:執行靜態邏輯,給靜態變量賦最終值,讓類真正 “激活”。
?
?
?
?
類加載器獲取
(1)方式 1:通過當前類的?getClassLoader()
?獲取
ClassLoader appClassLoader = ReflectTest.class.getClassLoader();
?
- 邏輯:
ReflectTest.class
?拿到當前類的?Class
?對象,調用?getClassLoader()
,獲取 “加載當前類的類加載器”。- 因?
ReflectTest
是項目 classpath 內的自定義類,加載它的就是?應用類加載器。
- 作用:驗證 “自定義類由應用類加載器加載”。
(2)方式 2:通過?ClassLoader.getSystemClassLoader()
?獲取
?
ClassLoader appClassLoader2 = ClassLoader.getSystemClassLoader();
?
- 邏輯:
ClassLoader
?是類加載器基類,靜態方法?getSystemClassLoader()
?直接返回?應用類加載器。- 這是 Java 提供的 “直接獲取應用類加載器” 的標準寫法。
- 作用:更直接獲取應用類加載器,與方式 1 對比,結果一致。
(3)方式 3:通過線程上下文類加載器獲取
?
ClassLoader appClassLoader3 = Thread.currentThread().getContextClassLoader();
System.out.println("應用類加載器:" + appClassLoader3);
?
?
- 邏輯:
- 每個 Java 線程默認有 “上下文類加載器(Context ClassLoader)”,默認就是?應用類加載器。
- 通過?
Thread.currentThread()
?拿到當前線程,調用?getContextClassLoader()
?獲取。
?