JVM只會運行二進制文件,類加載器的作用就是將字節碼文件加載到JVM中,從而讓Java程序能夠啟動起來。
1. 類加載器的種類
- 啟動類加載器(BootStrap ClassLoader):加載JAVA_HOME/jre/lib目錄下的庫
- 擴展類加載器(ExtClassLoader):主要加載JAVA_HOME/jre/lib/ext目錄中的類
- 應用類加載器(AppClassLoader):用于加載classPath下的類
- 自定義類加載器(CustomizeClassLoader):自定義類繼承ClassLoader,實現自定義類加載規則。
2. 雙親委派模型
2.1 原理
加載某一個類,先委托上一級的加載器進行加載,如果上級加載器也有上級,則會繼續向上委托,如果該類委托上級沒有被加載,子加載器嘗試加載該類
- student類在應用類加載器,向上委托,但lib和ext目錄都沒有student,所以向下由含有student類的子加載器(應用類加載器)加載student類
- string類在應用類加載器,向上委托,lib目錄下有,于是加載string類然后返回給應用類加載器讓它直接使用
2.2 使用場景
JVM為什么采用雙親委派機制?
- 通過雙親委派機制可以避免某一個類被重復加載,當父類已經加載后則無需重復加載,保證唯一性
- 為了安全,保證類庫API不會被修改
ex:
package java.long;
public class String{public static void main (Stringll args) {System.out.println("demo info");}
}
此時執行main函數,會出現異常,在類java.lang.String 中找不到 main 方法
錯誤:在類 java.Lang.string 中找不到 main 方法,請將 main 方法定義為:
public static void main(String[] args)
否則 JavaFX 應用程序類必須擴展
javafx.application.Application
報錯原因:由于是雙親委派的機制,javalang.String的在啟動類加載器得到加載,因為在核心jre庫中有其相同名字的類文件,但該類中并沒有main方法。這樣就能防止惡意篡改核心API庫。(所以這就是為什么類名不能是關鍵字的根本原因)
3. 類裝載的執行過程
- 類從加載到虛擬機中開始,直到卸載 止,它的整個生命周期包括了:加載、驗證、準備、解析、初始化、使用和卸載這7個階段。
- 其中,驗證、準備和解析這三個部分統稱為連接(linking)
3.1 加載階段
查找和導入class文件
- 通過類的全名,獲取類的二進制數據流。
- 解析類的二進制數據流為方法區內的數據結構(Java類模型)
- 創建java.lang.Class類的實例,表示該類型。作為方法區這個類的各種數據的訪問入口
3.2 連接階段
3.2.1 驗證階段
保證加載類的準確性
驗證類是否符合JVM規范,安全性檢查
- 格式檢查,如:文件格式是否錯誤、語法是否錯誤、字節碼是否合規(1)(2)(3)
- 文件格式驗證
- 元數據驗證
- 字節碼驗證
- Class文件在其常量池會通過字符串記錄自己將要使用的其他類或者方法,檢查它們是否存在(4)
4. 符號引用驗證
3.2.2 準備階段
內類變量分配內存并設置類變量初始值
- static變量,分配空間在準備階段完成(設置默認值),賦值在初始化階段完成
- static變量是final的基本類型,以及字符串常量,值已確定,賦值在準備階段完成
- static變量是final的引用類型,那么賦值也會在初始化階段完成
public class Application {static int b = 10; //(1)static final int c = 20; //(2)static final String d = "hello"; //(2)static final Object obj = new Object; //(3)
}
3.2.3 解析階段
把類中的符號引用轉換為直接引用
比如:方法中調用了其他方法,方法名可以理解為符號引用,而直接引用就是使用指針直接指向方法
- 符號引用:代#的是符號引用,符號引用可能也引用其他符號引用或直接引用類
- 直接引用:找到符號指的類與方法去執行
3.3 初始化階段
對類的靜態變量,靜態代碼塊執行初始化操作
- 如果初始化一個類的時候,其父類尚未初始化,則優先初始化其父類。
- 如果同時包含多個靜態變量和靜態代碼塊,則按照自上而下的順序依次執行。
//Animal是Cat父類
//子類初始化,如果父類還沒初始化,會引發父類先初始化
System.out.println(Cat.sex);
//子類訪問父類靜態變量,只觸發父類初始化
System.out.println(Cat.num);
3.4 使用階段
JVM 開始從入口方法開始執行用戶的程序代碼
- 調用靜態類成員信息(比如:靜態字段、靜態方法)
- 使用new關鍵字為其創建對象實例
3.5 卸載階段
當用戶程序代碼執行完畢后,JVM便開始銷毀創建的Class對象。