類(lèi)加載
類(lèi)加載子系統(tǒng)的作用
? 類(lèi)加載器子系統(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決定。加載的類(lèi)信息存放于一塊稱(chēng)為 方法區(qū) 的內(nèi)存空間。除了類(lèi)的信息外,方法區(qū)中還會(huì)存放運(yùn)行時(shí)常量池的信息,還可能包括字符串字面量和數(shù)字常量(這部分常量信息是class文件中常量池部分的內(nèi)存映射)。
類(lè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中被稱(chēng)為DNA元數(shù)據(jù)模板,放在方法區(qū)中。
在.class==》JVM==》最終稱(chēng)為元數(shù)據(jù)模板,此過(guò)程就要有一個(gè)運(yùn)輸工具(類(lèi)加載器ClassLoader),扮演一個(gè)快遞員的角色。
類(lèi)加載的過(guò)程
1.加載
通過(guò)類(lèi)名(地址)獲取此類(lèi)的二進(jìn)制字節(jié)流。
將這個(gè)字節(jié)流所代表的靜態(tài)存儲(chǔ)結(jié)構(gòu)轉(zhuǎn)換為方法區(qū)(元空間)的運(yùn)行時(shí)結(jié)構(gòu)。
在內(nèi)存中生成一個(gè)代表這個(gè)類(lèi)的java.lang.Class對(duì)象,作為方法區(qū)這個(gè)類(lèi)的各種數(shù)據(jù)的訪(fǎng)問(wèn)入口。
2.鏈接
驗(yàn)證:檢驗(yàn)被加載的類(lèi)是否有正確的內(nèi)部結(jié)構(gòu),并和其他類(lèi)協(xié)調(diào)一致。
準(zhǔn)備:準(zhǔn)備階段負(fù)責(zé)為類(lèi)的靜態(tài)屬性分配內(nèi)存,并設(shè)置默認(rèn)初始值。
? 不包含用final修飾的static實(shí)例變量,在編譯時(shí)進(jìn)行初始化。
? 不會(huì)為實(shí)例變量初始化。
解析:將類(lèi)的二進(jìn)制數(shù)據(jù)中的符號(hào)引用替換成直接引用(符號(hào)引用是用一組符號(hào)描述所引用的目標(biāo),直接引用是指向目標(biāo)的指針)。
3.初始化
類(lèi)在以下情況時(shí)會(huì)初始化:
創(chuàng)建類(lèi)的實(shí)例,也就是new一個(gè)對(duì)象。
訪(fǎng)問(wèn)某個(gè)類(lèi)或接口的靜態(tài)變量,或者對(duì)該靜態(tài)變量賦值。
調(diào)用類(lèi)的靜態(tài)方法。
反射。
初始化一個(gè)類(lèi)的子類(lèi)。
類(lèi)的初始化順序:
如果同時(shí)包含多個(gè)靜態(tài)變量和靜態(tài)代碼塊,則按照自上而下的順序依次執(zhí)行。
如果初始化一個(gè)類(lèi)的時(shí)候,其父類(lèi)尚未初始化,則優(yōu)先初始化其父類(lèi)。
順序是:父類(lèi)static --> 子類(lèi)static --> 父類(lèi)構(gòu)造方法 --> 子類(lèi)構(gòu)造方法
類(lèi)加載器分類(lèi)
JVM支持兩種類(lèi)型的類(lèi)加載器,分別為 引導(dǎo)類(lèi)加載器 和 自定義類(lèi)加載器。
? 從概念上講,自定義類(lèi)加載器一般指的時(shí)程序匯總由開(kāi)發(fā)人員自定義的一類(lèi)加載器,Java虛擬機(jī)將所有派生于抽象類(lèi)ClassLoader的類(lèi)加載器都劃分為自定義類(lèi)加載器。
常見(jiàn)的類(lèi)加載器有3個(gè):
引導(dǎo)類(lèi)加載器(啟動(dòng)類(lèi)加載器)
使用C/C++語(yǔ)言實(shí)現(xiàn),嵌套在JVM內(nèi)部,用來(lái)加載java核心類(lèi)庫(kù)。
并不集成于java.lang.ClassLoader,沒(méi)有父加載器。
負(fù)責(zé)加載擴(kuò)展類(lèi)加載器和應(yīng)用類(lèi)加載器,并為他們指定父類(lèi)加載器。
出于安全考慮,引用類(lèi)加載器只加載包名為java、javax、sun開(kāi)頭的類(lèi)。
擴(kuò)展類(lèi)加載器
使用java語(yǔ)言實(shí)現(xiàn)。
派生于ClassLoader類(lèi)。
上層類(lèi)加載器為引用類(lèi)加載器。
從java.ext.dirs系統(tǒng)屬性所指定的目錄中加載類(lèi)庫(kù)。或從JDK系統(tǒng)安裝目錄的jre/lib/ext子目錄(擴(kuò)展目錄)下加載類(lèi)庫(kù),如果用戶(hù)創(chuàng)建的jar放在此目錄下,也會(huì)自動(dòng)有狂戰(zhàn)類(lèi)加載器加載。
應(yīng)用程序類(lèi)加載器
使用java語(yǔ)言實(shí)現(xiàn)。
派生于ClassLoader類(lèi)。
上層類(lèi)加載器為擴(kuò)展類(lèi)加載器。
用來(lái)加載我們自己定義的類(lèi)。
該類(lèi)加載器時(shí)程序中默認(rèn)的類(lèi)加載器。
通過(guò)類(lèi)名.class.getClassLoader(),ClassLoader.getSystemClassLoader()來(lái)獲得。
ClassLoader是一個(gè)抽象類(lèi),其后所有的類(lèi)加載器都繼承自ClassLoader(不包括啟動(dòng)類(lèi)加載器)。
雙親委派機(jī)制
? Java虛擬機(jī)對(duì)class文件采用的時(shí)按需加載的方式,也就是說(shuō)當(dāng)需要該類(lèi)時(shí)才會(huì)將它的class文件加載到內(nèi)存中生成class對(duì)象。而且加載某個(gè)類(lèi)的class文件時(shí),Java虛擬機(jī)采用的時(shí)雙親委派機(jī)制,即把請(qǐng)求交由父類(lèi)處理,它是一種任務(wù)委派模式。
工作原理:
如果一個(gè)類(lèi)加載器收到了類(lèi)加載請(qǐng)求,并不會(huì)先去加載,而是把這個(gè)請(qǐng)求委托給父類(lèi)的加載器去執(zhí)行。
如果父類(lèi)加載器還存在其父類(lèi)加載器,則進(jìn)一步向上委托,一次遞歸,請(qǐng)求最終將到達(dá)頂層的啟動(dòng)類(lèi)加載器。
如果父類(lèi)加載器可以完成類(lèi)的加載任務(wù),就成功返回,倘若父類(lèi)加載器無(wú)法完成加載任務(wù),子加載器才會(huì)嘗試自己去加載,這就是雙親委派機(jī)制。
如果均加載失敗,就會(huì)拋出ClassNotFoundException異常。
**問(wèn)題:**創(chuàng)建一個(gè)名為java.lang的包和一個(gè)名為String的類(lèi),當(dāng)new String()時(shí),會(huì)加載核心庫(kù)類(lèi)中的String對(duì)象,還是自己創(chuàng)建的String類(lèi)對(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):
安全。可避免用戶(hù)自己編寫(xiě)的類(lèi)動(dòng)態(tài)替換Java核心類(lèi),如:java.lang.String
避免全限定命名的類(lèi)重復(fù)加載(使用了findLoadClass()判斷當(dāng)前類(lèi)是否已加載)
沙箱安全機(jī)制
作用:防止惡意代碼污染java源代碼
? 例如上面我們定義了一個(gè)名為String的類(lèi),所在包名為java.lang,因?yàn)檫@個(gè)類(lèi)本來(lái)是屬于jdk的,如果沒(méi)有沙箱安全機(jī)制的話(huà),這個(gè)類(lèi)將會(huì)污染到系統(tǒng)中的String,但是由于沙箱安全機(jī)制,所以就委托頂層的引導(dǎo)類(lèi)加載器查找到這個(gè)類(lèi),如果沒(méi)有的話(huà)就委托給擴(kuò)展類(lèi)加載器,再?zèng)]有就委托到系統(tǒng)類(lèi)加載器。但是由于String就是jdk的源代碼,所以在引導(dǎo)類(lèi)加載器那里就加載到了,先找到先使用,所以就使用引導(dǎo)類(lèi)加載器里面的String,后面的一概不能使用,這就保證了不被惡意代碼污染。
面試題:
在JVM中如何判斷兩個(gè)對(duì)象是屬于同一個(gè)類(lèi)?
答:1.類(lèi)的全類(lèi)名(地址)完全一致。 2.類(lèi)的加載器必須相同。
類(lèi)的主動(dòng)使用/被動(dòng)使用
JVM規(guī)定,每個(gè)類(lèi)或者接口被首次主動(dòng)使用時(shí)才對(duì)其進(jìn)行初始化,有主動(dòng)使用,自然就有被動(dòng)使用。
主動(dòng)使用和被動(dòng)使用的區(qū)別在于類(lèi)是否會(huì)被初始化。
主動(dòng)使用
通過(guò)new關(guān)鍵字使類(lèi)初始化,這是很常用的初始化一個(gè)類(lèi)的方式,肯定會(huì)使類(lèi)加載并且初始化。
訪(fǎng)問(wèn)類(lèi)的靜態(tài)變量,包括讀取和更新
訪(fǎng)問(wèn)類(lèi)的靜態(tài)方法
對(duì)某個(gè)類(lèi)進(jìn)行反射操作,會(huì)使類(lèi)初始化
初始化子類(lèi)會(huì)使其父類(lèi)初始化
執(zhí)行該類(lèi)的main方法
被動(dòng)使用
引用該類(lèi)的 靜態(tài)常量 ,不會(huì)導(dǎo)致初始化,但是也會(huì)有意外,這里的常量是指已經(jīng)指定字面量的常量,對(duì)于那些需要一些計(jì)算才能得出結(jié)果的常量就會(huì)引起類(lèi)的初始化。
例如:
public final static int num=5;//不會(huì)使類(lèi)初始化,被動(dòng)使用
public final static int random=new Random().nextInt();//會(huì)使類(lèi)初始化,主動(dòng)使用
構(gòu)造某個(gè)類(lèi)的數(shù)組時(shí)不會(huì)導(dǎo)致該類(lèi)的初始化
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)”用戶(hù)上傳并發(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.