java文件在解釋成class文件之后,需要對(duì)里面的類進(jìn)行初始化,初始化的過程包括加載、連接、裝入三個(gè)過程,在加載的過程中會(huì)對(duì)類里面的靜態(tài)變量進(jìn)行一個(gè)初始化。下面詳細(xì)介紹下ClassLoader: 1、類的加載、連接和初始化 類初始化通常包括加載、連接、初始化三個(gè)步驟。 (1)進(jìn)程的結(jié)束 每當(dāng)運(yùn)行一個(gè)java程序時(shí),將會(huì)啟動(dòng)一個(gè)java虛擬機(jī)進(jìn)程,不管程序多么復(fù)雜,有多少線程,都在這個(gè)java虛擬機(jī)進(jìn)程里。以下四種情況會(huì)使得該進(jìn)程被終止—— 程序運(yùn)行到最后正常結(jié)束; 程序里遭遇了System.exit(),或者是Runtime.getRunTime().exit()代碼;程序執(zhí)行中遇到了未捕獲的異?;蛘咤e(cuò)誤; java所在平臺(tái)強(qiáng)制結(jié)束了JVM進(jìn)程; 當(dāng)該進(jìn)程結(jié)束,那么該進(jìn)程在內(nèi)存中的狀態(tài)將會(huì)丟失,包括靜態(tài)變量的值。 (2)類的加載 類的加載是指將類的class文件讀入內(nèi)存,并為之創(chuàng)建一個(gè)java.lang.class對(duì)象。 (3)類的連接 類的連接負(fù)責(zé)把類的二進(jìn)制數(shù)據(jù)合并到JRE中,分為三個(gè)階段—— 驗(yàn)證:檢驗(yàn)被加載的類是否有正確的內(nèi)部結(jié)構(gòu); 準(zhǔn)備:負(fù)責(zé)為類的靜態(tài)Field分配內(nèi)存,并設(shè)置默認(rèn)初始值;解析:將類的二進(jìn)制數(shù)據(jù)中的符號(hào)應(yīng)用替換成直接引用。 (4)類的初始化 類的初始化,主要就是對(duì)靜態(tài)Field進(jìn)行初始化。 2、類加載器 (1)類加載器簡(jiǎn)介 類加載器負(fù)責(zé)加載所有的類,系統(tǒng)為所有載入內(nèi)存里的類都會(huì)生成一個(gè)java.lang.class對(duì)象; 同一個(gè)類只會(huì)被加載一次,在JVM中每一個(gè)不同的類都會(huì)有一個(gè)不同的類加載器負(fù)責(zé)。 類加載器在不包括用戶自定義的加載器的情況下包括三層: Bootstrap Classloader根加載器; ExtensionClassloader擴(kuò)展類加載器; SystemClassLoader系統(tǒng)類加載器; ①根加載器,又稱為引導(dǎo)或者原始加載器,負(fù)責(zé)加載java的核心類; ②擴(kuò)展類加載器,負(fù)責(zé)加載JRE的擴(kuò)展目錄中JAR包的類; ③系統(tǒng)類加載器,又稱為應(yīng)用加載器, 負(fù)責(zé)JVM啟動(dòng)時(shí)加載來(lái)自java命令的-classpath或者CLASSPATH環(huán)境變量所指定的JAR包和類路徑。一般是程序運(yùn)行的當(dāng)前路徑。所以其稱為應(yīng)用類加載器。 三個(gè)類加載器的順序是: bootstrap classloader | extension classloader | system classloader 如果有用戶自定義的類加載器,那么在系統(tǒng)加載器后,將會(huì)執(zhí)行用戶類加載器。 (2)自定義類加載器 如果我們需要在啟動(dòng)類加載時(shí)作一些特定需求的行為,那么就需要自定義類加載器了。 自定義ClassLoader需要繼承ClassLoader抽象類,重寫findClass方法,這個(gè)方法定義了ClassLoader查找class的方式。 主要可以擴(kuò)展的方法有: findClass 定義查找Class的方式 defineClass 將類文件字節(jié)碼加載為jvm中的class findResource 定義查找資源的方式 (3)類加載機(jī)制 JVM的類加載機(jī)制有以下三種: 全盤負(fù)責(zé):所謂全盤負(fù)責(zé),即是當(dāng)一個(gè)classloader加載一個(gè)Class的時(shí)候,這個(gè)Class所依賴的和引用的所有Class也由這個(gè)classloader負(fù)責(zé)載入,除非是顯式的使用另外一個(gè)classloader載入。 父類委托:所謂父類委托,就是先讓父類加載器先嘗試加載該Class,當(dāng)父類無(wú)法加載的時(shí)候,才是嘗試從自己的類路徑中去加載。JVM的ClassLoader采用的是樹形結(jié)構(gòu),除了BootstrapClassLoader以外,每個(gè)ClassLoader都會(huì)有一個(gè)parentClassLoader,即父類加載器,用戶自定義的ClassLoader默認(rèn)的parendClassLoader是SystemClassLoader,當(dāng)然你可以自己指定需要用哪一個(gè)ClassLoader的實(shí)例 緩存機(jī)制:所謂緩存機(jī)制就是保證所有加載過的類都會(huì)被緩存,當(dāng)程序中需要某個(gè)類時(shí),會(huì)先從緩存區(qū)中搜查該類,當(dāng)緩存區(qū)不存在該類對(duì)象時(shí),系統(tǒng)才會(huì)讀取該類的二進(jìn)制文件。 4、一些重要的方法 (1)loadClass方法 ClassLoader.loadClass()是ClassLoader的入口點(diǎn)。該方法的定義如下: Class loadClass(String name,boolean resolve); name是加載的類的名稱,resolve是告訴方法是不中需要解析類PS:并不是所有的類都需要解析,如果JVM只想知道這個(gè)類是否存在或找出該類的超類,那么就不需要解析該類 (2)defineClass方法 defineClass方法接受由原始字節(jié)組成的數(shù)組,并把它轉(zhuǎn)換成Class的對(duì)象。原始數(shù)組包含如從文件系統(tǒng)或網(wǎng)絡(luò)裝入的數(shù)據(jù)。defineClass管理JVM的許多復(fù)雜的實(shí)現(xiàn)層面——它把字節(jié)碼分析成運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu)、校驗(yàn)有效性等,因?yàn)閐efineClass方法被標(biāo)記成final的,所以不能覆蓋它。 (3)findSytemClass方法 findSystemClass方法就是查找本的類Class文件,然后裝入 (4)resolveClass方法 我們?cè)谡{(diào)用編寫自己的loadClass方法的時(shí)候可以調(diào)用resolveClass方法來(lái)獲得resolve參數(shù) (5)findLoadedClass方法 在調(diào)用loadClass方法之前可以調(diào)用改方法來(lái)查看地ClassLoader是否已經(jīng)裝入了這個(gè)類,這樣可以避免重新裝入這個(gè)類 (6)findClass方法 在loadClass默認(rèn)實(shí)現(xiàn)調(diào)用這個(gè)新方法。findClass的用途包含classLoader的所有特殊代碼,而無(wú)須復(fù)制其他代碼 (7)getSystemClassLoader方法 在如果覆蓋findClass或loadClass,getSystemClassLoader能以實(shí)際的ClassLoader對(duì)象訪問系統(tǒng)ClassLoader(而不是固定地從findSystemClass調(diào)用它)。為了將類請(qǐng)求委托給父類ClassLoader,這個(gè)新方法允許ClassLoader獲取它的父類ClassLoader.當(dāng)使用特殊方法,定制的ClassLoader不能找到類時(shí),可以使用這種方法。 父類ClassLoader被定義成創(chuàng)建該ClassLoader所包含代碼的對(duì)象的ClassLoader. (8)forName方法 在Class類中有一個(gè)靜態(tài)方法forName,這個(gè)方法和ClassLoader中的loaderClass方法的目的是一樣的,都是用來(lái)加載Class的,但是兩者在作用上卻有所區(qū)別: loadClass加載實(shí)際上就是加載的時(shí)候并不對(duì)該類進(jìn)行解釋,因此不會(huì)初始化該類。而Class類的forName方法則相反,使用forName加載的時(shí)候就會(huì)將Class進(jìn)行解釋和初始化
|