類載入器及類的初始化流程
類的初始化一共有三個階段:類載入、連結、初始化
類載入
類載入的過程,就是將位元組流載入進JVM方法區並生成Class物件的過程。類載入過程是通過類載入器實現的,主要有三個主要的類載入器:
* bootstrap class loader:根類載入器,這個載入器不是Java語言實現的所以沒有具體的類,它用來載入最主要的類,例如jre下lib目錄下的類
* 擴充套件類載入器:Classloader的子類,用來載入非主要的類,例如jre擴充套件包下的類。
* 應用類載入器:載入classpath環境下的jar包路徑。基本上所有的自定義類載入器都是應用類載入器的子類
- 在這裡借用一張類載入器的關係圖,可以清晰的說明類載入器之間的關係。
- 啟動類載入器是根類載入器,擴充套件類載入器的父載入器是啟動類載入器,應用類的父載入器是擴充套件類載入器,自定義類載入器的父載入器是應用類載入器。(概念不能弄混:父載入器並不是父類,任何parent為null的載入器,它的父載入器都是BootStrapClassLoader)。
- 什麼是雙親委派模式:當一個類接收到類載入請求時,類載入器會在快取中查詢類是否已經被載入過,如果沒有找到就會將類載入請求移交給父載入器,父載入器也會做同樣的判斷和篩查進行一層層的,直到啟動類載入器。如果啟動類載入器也沒有找到類,啟動類會判斷這個類是否是自己載入的,如果不是就會將載入類的請求發放給子類載入器,子類載入器重複查詢和判斷直到最底層的類載入器,如果最底層類載入器沒有查詢到類資訊就會丟擲ClassNotFoundException。
- 為什麼要使用雙親委派模式:為了保證類的全域性唯一性,保證核心API庫的安全性。啟動類載入器用來載入jre中lib下的類,如果我們手動建立了一個和lang包下重名的類,這個類是不會被載入的。
- 補充:類載入器 + 類的路徑對應一個為一個Class物件。
連結
一共分為三個階段
* 驗證:用於檢查被載入的類是否可以正確被載入,例如許可權修飾符的檢驗。
* 準備:被靜態欄位分配記憶體,並賦予零值。
* 解析:將符號引用替換為地址引用。
初始化
為靜態欄位進行賦值。靜態欄位被標記為final,且欄位型別是基本型別或String時,靜態欄位會在編譯過程中完成賦值。其他情況被標記的靜態欄位或靜態程式碼塊,會被放置到clinit函式中進行初始化,JVM通過加鎖的方式保證clinit函式只被執行一次,即靜態資料只被載入一次。
類的載入時機是什麼?
類的載入機制如下:
* new一個物件的時候
* 查詢一個類的靜態欄位,或給一個類的靜態欄位賦值的時候
* 呼叫一個類的靜態方法的時候
* 反射機制
* 初始化一個類的子類