類加載
類加載子系統的作用

? 類加載器子系統負責從檔案系統或者網路中加載class檔案,class檔案在檔案開頭有特定的檔案標識(位元組碼檔案都以CA FE BA BE標識開頭),
? ClassLoader只負責class檔案的加載,至于它是否可以運行,則由Execution Engine決定,加載的類資訊存放于一塊稱為 方法區 的記憶體空間,除了類的資訊外,方法區中還會存放運行時常量池的資訊,還可能包括字串字面量和數字常量(這部分常量資訊是class檔案中常量池部分的記憶體映射),
類加載ClassLoader的角色

- class file存在于硬碟上,可以理解為設計師畫在紙上的模板,而最終這個模板在執行的時候是要加載到JVM當中來,根據這個模板實體化出n個一模一樣的實體,
- class file加載到JVM中被稱為DNA元資料模板,放在方法區中,
- 在.class==》JVM==》最終稱為元資料模板,此程序就要有一個運輸工具(類加載器ClassLoader),扮演一個快遞員的角色,
類加載的程序

1.加載
- 通過類名(地址)獲取此類的二進制位元組流,
- 將這個位元組流所代表的靜態存盤結構轉換為方法區(元空間)的運行時結構,
- 在記憶體中生成一個代表這個類的java.lang.Class物件,作為方法區這個類的各種資料的訪問入口,
2.鏈接
-
驗證:檢驗被加載的類是否有正確的內部結構,并和其他類協調一致,
-
準備:準備階段負責為類的靜態屬性分配記憶體,并設定默認初始值,
? 不包含用final修飾的static實體變數,在編譯時進行初始化,
? 不會為實體變數初始化,
-
決議:將類的二進制資料中的符號參考替換成直接參考(符號參考是用一組符號描述所參考的目標,直接參考是指向目標的指標),
3.初始化
類在以下情況時會初始化:
- 創建類的實體,也就是new一個物件,
- 訪問某個類或介面的靜態變數,或者對該靜態變數賦值,
- 呼叫類的靜態方法,
- 反射,
- 初始化一個類的子類,
類的初始化順序:
- 如果同時包含多個靜態變數和靜態代碼塊,則按照自上而下的順序依次執行,
- 如果初始化一個類的時候,其父類尚未初始化,則優先初始化其父類,
- 順序是:父類static --> 子類static --> 父類構造方法 --> 子類構造方法
類加載器分類
JVM支持兩種型別的類加載器,分別為 引導類加載器 和 自定義類加載器,
? 從概念上講,自定義類加載器一般指的時程式匯總由開發人員自定義的一類加載器,Java虛擬機將所有派生于抽象類ClassLoader的類加載器都劃分為自定義類加載器,
常見的類加載器有3個:
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-uhAwHUR1-1617787397052)(C:\Users\huawei\AppData\Roaming\Typora\typora-user-images\image-20210406171147333.png)]](https://img.uj5u.com/2021/04/08/234406081124414.png)
引導類加載器(啟動類加載器)
- 使用C/C++語言實作,嵌套在JVM內部,用來加載java核心類別庫,
- 并不集成于java.lang.ClassLoader,沒有父加載器,
- 負責加載擴展類加載器和應用類加載器,并為他們指定父類加載器,
- 出于安全考慮,參考類加載器只加載包名為java、javax、sun開頭的類,
擴展類加載器
- 使用java語言實作,
- 派生于ClassLoader類,
- 上層類加載器為參考類加載器,
- 從java.ext.dirs系統屬性所指定的目錄中加載類別庫,或從JDK系統安裝目錄的jre/lib/ext子目錄(擴展目錄)下加載類別庫,如果用戶創建的jar放在此目錄下,也會自動有狂戰類加載器加載,
應用程式類加載器
- 使用java語言實作,
- 派生于ClassLoader類,
- 上層類加載器為擴展類加載器,
- 用來加載我們自己定義的類,
- 該類加載器時程式中默認的類加載器,
- 通過類名.class.getClassLoader(),ClassLoader.getSystemClassLoader()來獲得,
ClassLoader是一個抽象類,其后所有的類加載器都繼承自ClassLoader(不包括啟動類加載器),
雙親委派機制
? Java虛擬機對class檔案采用的時按需加載的方式,也就是說當需要該類時才會將它的class檔案加載到記憶體中生成class物件,而且加載某個類的class檔案時,Java虛擬機采用的時雙親委派機制,即把請求交由父類處理,它是一種任務委派模式,
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-oH3nGKsX-1617787397054)(C:\Users\huawei\AppData\Roaming\Typora\typora-user-images\image-20210406173957779.png)]](https://img.uj5u.com/2021/04/08/234406081124415.png)
作業原理:
- 如果一個類加載器收到了類加載請求,并不會先去加載,而是把這個請求委托給父類的加載器去執行,
- 如果父類加載器還存在其父類加載器,則進一步向上委托,一次遞回,請求最終將到達頂層的啟動類加載器,
- 如果父類加載器可以完成類的加載任務,就成功回傳,倘若父類加載器無法完成加載任務,子加載器才會嘗試自己去加載,這就是雙親委派機制,
如果均加載失敗,就會拋出ClassNotFoundException例外,
**問題:**創建一個名為java.lang的包和一個名為String的類,當new String()時,會加載核心庫類中的String物件,還是自己創建的String類物件呢?
package java.lang; //此包為自己創建的java.lang
public class String{
public static void main(String[] args){
System.out.println("hello");
}
}
//這段代碼能否執行呢?(運行不了)
雙親委派機制的優點:
- 安全,可避免用戶自己撰寫的類動態替換Java核心類,如:java.lang.String
- 避免全限定命名的類重復加載(使用了findLoadClass()判斷當前類是否已加載)
沙箱安全機制
作用:防止惡意代碼污染java源代碼
? 例如上面我們定義了一個名為String的類,所在包名為java.lang,因為這個類本來是屬于jdk的,如果沒有沙箱安全機制的話,這個類將會污染到系統中的String,但是由于沙箱安全機制,所以就委托頂層的引導類加載器查找到這個類,如果沒有的話就委托給擴展類加載器,再沒有就委托到系統類加載器,但是由于String就是jdk的源代碼,所以在引導類加載器那里就加載到了,先找到先使用,所以就使用引導類加載器里面的String,后面的一概不能使用,這就保證了不被惡意代碼污染,
面試題:
在JVM中如何判斷兩個物件是屬于同一個類?
答:1.類的全類名(地址)完全一致, 2.類的加載器必須相同,
類的主動使用/被動使用
JVM規定,每個類或者介面被首次主動使用時才對其進行初始化,有主動使用,自然就有被動使用,
主動使用和被動使用的區別在于類是否會被初始化,
-
主動使用
- 通過new關鍵字使類初始化,這是很常用的初始化一個類的方式,肯定會使類加載并且初始化,
- 訪問類的靜態變數,包括讀取和更新
- 訪問類的靜態方法
- 對某個類進行反射操作,會使類初始化
- 初始化子類會使其父類初始化
- 執行該類的main方法
-
被動使用
-
參考該類的 靜態常量 ,不會導致初始化,但是也會有意外,這里的常量是指已經指定字面量的常量,對于那些需要一些計算才能得出結果的常量就會引起類的初始化,
例如:
public final static int num=5;//不會使類初始化,被動使用 public final static int random=new Random().nextInt();//會使類初始化,主動使用 -
構造某個類的陣列時不會導致該類的初始化
Student[] student = new Student[10];
-
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/273704.html
標籤:java
