類加載
類加載子系統(tǒng)的作用
? 類加載器子系統(tǒng)負(fù)責(zé)從文件系統(tǒng)或者網(wǎng)絡(luò)中加載class文件,class文件在文件開(kāi)頭有特定的文件標(biāo)識(shí)(字節(jié)碼文件都以CA FE BA BE標(biāo)識(shí)開(kāi)頭)。
? ClassLoader只負(fù)責(zé)class文件的加載,至于它是否可以運(yùn)行,則由Execution Engine決定。加載的類信息存放于一塊稱為 方法區(qū) 的內(nèi)存空間。除了類的信息外,方法區(qū)中還會(huì)存放運(yùn)行時(shí)常量池的信息,還可能包括字符串字面量和數(shù)字常量(這部分常量信息是class文件中常量池部分的內(nèi)存映射)。
類加載ClassLoader的角色
class file存在于硬盤(pán)上,可以理解為設(shè)計(jì)師畫(huà)在紙上的模板,而最終這個(gè)模板在執(zhí)行的時(shí)候是要加載到JVM當(dāng)中來(lái),根據(jù)這個(gè)模板實(shí)例化出n個(gè)一模一樣的實(shí)例。
class file加載到JVM中被稱為DNA元數(shù)據(jù)模板,放在方法區(qū)中。
在.class==》JVM==》最終稱為元數(shù)據(jù)模板,此過(guò)程就要有一個(gè)運(yùn)輸工具(類加載器ClassLoader),扮演一個(gè)快遞員的角色。
類加載的過(guò)程
1.加載
通過(guò)類名(地址)獲取此類的二進(jìn)制字節(jié)流。
將這個(gè)字節(jié)流所代表的靜態(tài)存儲(chǔ)結(jié)構(gòu)轉(zhuǎn)換為方法區(qū)(元空間)的運(yùn)行時(shí)結(jié)構(gòu)。
在內(nèi)存中生成一個(gè)代表這個(gè)類的java.lang.Class對(duì)象,作為方法區(qū)這個(gè)類的各種數(shù)據(jù)的訪問(wèn)入口。
2.鏈接
驗(yàn)證:檢驗(yàn)被加載的類是否有正確的內(nèi)部結(jié)構(gòu),并和其他類協(xié)調(diào)一致。
準(zhǔn)備:準(zhǔn)備階段負(fù)責(zé)為類的靜態(tài)屬性分配內(nèi)存,并設(shè)置默認(rèn)初始值。
? 不包含用final修飾的static實(shí)例變量,在編譯時(shí)進(jìn)行初始化。
? 不會(huì)為實(shí)例變量初始化。
解析:將類的二進(jìn)制數(shù)據(jù)中的符號(hào)引用替換成直接引用(符號(hào)引用是用一組符號(hào)描述所引用的目標(biāo),直接引用是指向目標(biāo)的指針)。
3.初始化
類在以下情況時(shí)會(huì)初始化:
創(chuàng)建類的實(shí)例,也就是new一個(gè)對(duì)象。
訪問(wèn)某個(gè)類或接口的靜態(tài)變量,或者對(duì)該靜態(tài)變量賦值。
調(diào)用類的靜態(tài)方法。
反射。
初始化一個(gè)類的子類。
類的初始化順序:
如果同時(shí)包含多個(gè)靜態(tài)變量和靜態(tài)代碼塊,則按照自上而下的順序依次執(zhí)行。
如果初始化一個(gè)類的時(shí)候,其父類尚未初始化,則優(yōu)先初始化其父類。
順序是:父類static --> 子類static --> 父類構(gòu)造方法 --> 子類構(gòu)造方法
類加載器分類
JVM支持兩種類型的類加載器,分別為 引導(dǎo)類加載器 和 自定義類加載器。
? 從概念上講,自定義類加載器一般指的時(shí)程序匯總由開(kāi)發(fā)人員自定義的一類加載器,Java虛擬機(jī)將所有派生于抽象類ClassLoader的類加載器都劃分為自定義類加載器。
常見(jiàn)的類加載器有3個(gè):
引導(dǎo)類加載器(啟動(dòng)類加載器)
使用C/C++語(yǔ)言實(shí)現(xiàn),嵌套在JVM內(nèi)部,用來(lái)加載java核心類庫(kù)。
并不集成于java.lang.ClassLoader,沒(méi)有父加載器。
負(fù)責(zé)加載擴(kuò)展類加載器和應(yīng)用類加載器,并為他們指定父類加載器。
出于安全考慮,引用類加載器只加載包名為java、javax、sun開(kāi)頭的類。
擴(kuò)展類加載器
使用java語(yǔ)言實(shí)現(xiàn)。
派生于ClassLoader類。
上層類加載器為引用類加載器。
從java.ext.dirs系統(tǒng)屬性所指定的目錄中加載類庫(kù)。或從JDK系統(tǒng)安裝目錄的jre/lib/ext子目錄(擴(kuò)展目錄)下加載類庫(kù),如果用戶創(chuàng)建的jar放在此目錄下,也會(huì)自動(dòng)有狂戰(zhàn)類加載器加載。
應(yīng)用程序類加載器
使用java語(yǔ)言實(shí)現(xiàn)。
派生于ClassLoader類。
上層類加載器為擴(kuò)展類加載器。
用來(lái)加載我們自己定義的類。
該類加載器時(shí)程序中默認(rèn)的類加載器。
通過(guò)類名.class.getClassLoader(),ClassLoader.getSystemClassLoader()來(lái)獲得。
ClassLoader是一個(gè)抽象類,其后所有的類加載器都繼承自ClassLoader(不包括啟動(dòng)類加載器)。
雙親委派機(jī)制
? Java虛擬機(jī)對(duì)class文件采用的時(shí)按需加載的方式,也就是說(shuō)當(dāng)需要該類時(shí)才會(huì)將它的class文件加載到內(nèi)存中生成class對(duì)象。而且加載某個(gè)類的class文件時(shí),Java虛擬機(jī)采用的時(shí)雙親委派機(jī)制,即把請(qǐng)求交由父類處理,它是一種任務(wù)委派模式。
工作原理:
如果一個(gè)類加載器收到了類加載請(qǐng)求,并不會(huì)先去加載,而是把這個(gè)請(qǐng)求委托給父類的加載器去執(zhí)行。
如果父類加載器還存在其父類加載器,則進(jìn)一步向上委托,一次遞歸,請(qǐng)求最終將到達(dá)頂層的啟動(dòng)類加載器。
如果父類加載器可以完成類的加載任務(wù),就成功返回,倘若父類加載器無(wú)法完成加載任務(wù),子加載器才會(huì)嘗試自己去加載,這就是雙親委派機(jī)制。
如果均加載失敗,就會(huì)拋出ClassNotFoundException異常。
**問(wèn)題:**創(chuàng)建一個(gè)名為java.lang的包和一個(gè)名為String的類,當(dāng)new String()時(shí),會(huì)加載核心庫(kù)類中的String對(duì)象,還是自己創(chuàng)建的String類對(duì)象呢?
package java.lang; //此包為自己創(chuàng)建的java.lang
public class String{
public static void main(String[] args){
System.out.println("hello");
}
}
//這段代碼能否執(zhí)行呢?(運(yùn)行不了)
雙親委派機(jī)制的優(yōu)點(diǎn):
安全。可避免用戶自己編寫(xiě)的類動(dòng)態(tài)替換Java核心類,如:java.lang.String
避免全限定命名的類重復(fù)加載(使用了findLoadClass()判斷當(dāng)前類是否已加載)
沙箱安全機(jī)制
作用:防止惡意代碼污染java源代碼
? 例如上面我們定義了一個(gè)名為String的類,所在包名為java.lang,因?yàn)檫@個(gè)類本來(lái)是屬于jdk的,如果沒(méi)有沙箱安全機(jī)制的話,這個(gè)類將會(huì)污染到系統(tǒng)中的String,但是由于沙箱安全機(jī)制,所以就委托頂層的引導(dǎo)類加載器查找到這個(gè)類,如果沒(méi)有的話就委托給擴(kuò)展類加載器,再?zèng)]有就委托到系統(tǒng)類加載器。但是由于String就是jdk的源代碼,所以在引導(dǎo)類加載器那里就加載到了,先找到先使用,所以就使用引導(dǎo)類加載器里面的String,后面的一概不能使用,這就保證了不被惡意代碼污染。
面試題:
在JVM中如何判斷兩個(gè)對(duì)象是屬于同一個(gè)類?
答:1.類的全類名(地址)完全一致。 2.類的加載器必須相同。
類的主動(dòng)使用/被動(dòng)使用
JVM規(guī)定,每個(gè)類或者接口被首次主動(dòng)使用時(shí)才對(duì)其進(jìn)行初始化,有主動(dòng)使用,自然就有被動(dòng)使用。
主動(dòng)使用和被動(dòng)使用的區(qū)別在于類是否會(huì)被初始化。
主動(dòng)使用
通過(guò)new關(guān)鍵字使類初始化,這是很常用的初始化一個(gè)類的方式,肯定會(huì)使類加載并且初始化。
訪問(wèn)類的靜態(tài)變量,包括讀取和更新
訪問(wèn)類的靜態(tài)方法
對(duì)某個(gè)類進(jìn)行反射操作,會(huì)使類初始化
初始化子類會(huì)使其父類初始化
執(zhí)行該類的main方法
被動(dòng)使用
引用該類的 靜態(tài)常量 ,不會(huì)導(dǎo)致初始化,但是也會(huì)有意外,這里的常量是指已經(jīng)指定字面量的常量,對(duì)于那些需要一些計(jì)算才能得出結(jié)果的常量就會(huì)引起類的初始化。
例如:
public final static int num=5;//不會(huì)使類初始化,被動(dòng)使用
public final static int random=new Random().nextInt();//會(huì)使類初始化,主動(dòng)使用
構(gòu)造某個(gè)類的數(shù)組時(shí)不會(huì)導(dǎo)致該類的初始化
Student[] student = new Student[10];
————————————————
版權(quán)聲明:本文為CSDN博主「Gxbalun喬峰」的原創(chuàng)文章,遵循CC 4.0 BY-SA版權(quán)協(xié)議,轉(zhuǎn)載請(qǐng)附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/weixin_45636230/article/details/115490932
特別聲明:以上內(nèi)容(如有圖片或視頻亦包括在內(nèi))為自媒體平臺(tái)“網(wǎng)易號(hào)”用戶上傳并發(fā)布,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.