|
上一篇,我們?cè)敿?xì)講解了java類的加載機(jī)制,本篇我們繼續(xù)講解java類加載器的內(nèi)容。 一、 什么是類加載器? 二、類加載器的樹狀層次結(jié)構(gòu) Java 中的類加載器大致可以分成兩類,一類是系統(tǒng)提供的,另外一類則是由 Java 應(yīng)用開發(fā)人員編寫的。 類加載器的樹狀層次結(jié)構(gòu)圖 1.引導(dǎo)類加載器(bootstrap class loader): 它用來加載 Java 的核心庫(jre/lib/rt.jar),是用原生C++代碼來實(shí)現(xiàn)的,并不繼承自java.lang.ClassLoader。加載擴(kuò)展類和應(yīng)用程序類加載器,并指定他們的父類加載器,在java中獲取不到。 2.擴(kuò)展類加載器(extensions class loader): 它用來加載 Java 的擴(kuò)展庫(jre/ext/*.jar)。Java 虛擬機(jī)的實(shí)現(xiàn)會(huì)提供一個(gè)擴(kuò)展庫目錄。該類加載器在此目錄里面查找并加載 Java 類。 3.系統(tǒng)類加載器(system class loader): 它根據(jù) Java 應(yīng)用的類路徑(CLASSPATH)來加載 Java 類。一般來說,Java 應(yīng)用的類都是由它來完成加載的??梢酝ㄟ^ 4.自定義類加載器(custom class loader): 除了系統(tǒng)提供的類加載器以外,開發(fā)人員可以通過繼承 java.lang.ClassLoader類的方式實(shí)現(xiàn)自己的類加載器,以滿足一些特殊的需求。 測(cè)試代碼案例:
輸出結(jié)果: 三、類加載器的工作機(jī)制 虛擬機(jī)把描述類的數(shù)據(jù)從Class文件加載到內(nèi)存,并對(duì)數(shù)據(jù)進(jìn)行校驗(yàn)、轉(zhuǎn)換解析和初始化,最終形成可以被虛擬機(jī)直接使用的Java類型,這就是虛擬機(jī)的類加載機(jī)制。 類加載器就是尋找類的字節(jié)碼文件并構(gòu)造出類在JVM內(nèi)部表示的對(duì)象組件。在java中,類加載器把一個(gè)類裝入JVM中,要經(jīng)過以下步驟: 1.裝載:查找和導(dǎo)入Class文件; 2.鏈接:執(zhí)行校驗(yàn)、準(zhǔn)備和解析步驟,其中解析步驟是可以選擇的; (1)校驗(yàn):檢查載入Class文件數(shù)據(jù)的正確性; (2)準(zhǔn)備:給累的靜態(tài)變量分配存儲(chǔ)空間; (3)解析:將符號(hào)引用轉(zhuǎn)成直接引用; 3.初始化:對(duì)類的靜態(tài)變量、靜態(tài)代碼塊執(zhí)行初始化工作。 類加載工作由ClassLoader及其子類負(fù)責(zé),ClassLoader是一個(gè)重要的java運(yùn)行時(shí)系統(tǒng)組件,他負(fù)責(zé)在運(yùn)行時(shí)查找和裝入Class字節(jié)碼文件。JVM在運(yùn)行時(shí)會(huì)產(chǎn)生三個(gè)ClassLoader:根裝載器、擴(kuò)展類加載器和應(yīng)用程序類加載器。其中根裝載器不是ClassLoader的子類,它是由C++語言編寫,因此我們?cè)趈ava中看不到它。根裝載器負(fù)責(zé)裝載JRE的核心類庫,如JRE目標(biāo)下的大rt.jar、charsets.jar等。擴(kuò)展類加載器和應(yīng)用程序類加載器都是ClassLoader的子類,其中擴(kuò)展類加載器負(fù)責(zé)裝載JRE擴(kuò)展目錄ext中的JAR類包;應(yīng)用程序類加載器負(fù)責(zé)加載Classpath路徑下的類包。 四、雙親委派模型 雙親委派模型過程 某個(gè)特定的類加載器在接到加載類的請(qǐng)求時(shí),首先將加載任務(wù)委托給父類加載器,依次遞歸,如果父類加載器可以完成類加載任務(wù),就成功返回;只有父類加載器無法完成此加載任務(wù)時(shí),才自己去加載。 使用雙親委派模型的好處在于Java類隨著它的類加載器一起具備了一種帶有優(yōu)先級(jí)的層次關(guān)系。例如類java.lang.Object,它存在在rt.jar中,無論哪一個(gè)類加載器要加載這個(gè)類,最終都是委派給處于模型最頂端的Bootstrap ClassLoader進(jìn)行加載,因此Object類在程序的各種類加載器環(huán)境中都是同一個(gè)類。相反,如果沒有雙親委派模型而是由各個(gè)類加載器自行加載的話,如果用戶編寫了一個(gè)java.lang.Object的同名類并放在ClassPath中,那系統(tǒng)中將會(huì)出現(xiàn)多個(gè)不同的Object類,程序?qū)⒒靵y。因此,如果開發(fā)者嘗試編寫一個(gè)與rt.jar類庫中重名的Java類,可以正常編譯,但是永遠(yuǎn)無法被加載運(yùn)行。 雙親委派模型的系統(tǒng)實(shí)現(xiàn) 在java.lang.ClassLoader的loadClass()方法中,先檢查是否已經(jīng)被加載過,若沒有加載則調(diào)用父類加載器的loadClass()方法,若父加載器為空則默認(rèn)使用啟動(dòng)類加載器作為父加載器。如果父加載失敗,則拋出ClassNotFoundException異常后,再調(diào)用自己的findClass()方法進(jìn)行加載。
五、類加載器的過程 首先我們先看一下類加載器過程圖,如下:
java類加載器過程圖 類加載器的過程包括了三個(gè)大步驟,他們分別是: 加載 1.加載指的是將類的class文件讀入到內(nèi)存,并為之創(chuàng)建一個(gè)java.lang.Class對(duì)象。 2.通過類的全限定名獲取此類的二進(jìn)制字節(jié)流。 3.將這個(gè)字節(jié)流所代表的靜態(tài)存儲(chǔ)結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu)。 4.在內(nèi)存中生成一個(gè)Class對(duì)象,作為方法區(qū)這個(gè)類的各種數(shù)據(jù)的訪問入口。 加載通常由類加載器完成,加載類的方式具體有以下幾項(xiàng): 1.本地資源加載。 2.網(wǎng)絡(luò)加載。Web Applet。 3.zip壓縮包加載。jar,war。 4.運(yùn)行時(shí)計(jì)算生成。動(dòng)態(tài)代理技術(shù)。 5.其他文件生成。JSP應(yīng)用。 6.從加密文件中讀取,主要是為了防止class文件被反編譯。 鏈接 鏈接分為三個(gè)步驟: 1.驗(yàn)證 確保class文件的字節(jié)流中包含信息符合虛擬機(jī)要求。 主要包括,文件格式驗(yàn)證,元數(shù)據(jù)驗(yàn)證,字節(jié)碼驗(yàn)證,符號(hào)引用驗(yàn)證。 2.準(zhǔn)備 為類變量分配內(nèi)存,并為變量賦零值。 這里不包括用final修飾的static,因?yàn)閒inal在變異的時(shí)候就會(huì)分配了,準(zhǔn)備階段會(huì)顯式初始化。不會(huì)為實(shí)例變量分配初始化。 3.解析 將常量池內(nèi)的符號(hào)引用變?yōu)橹苯右谩?/p> 初始化 初始化階段就是執(zhí)行類構(gòu)造器方法()方法。 ()方法是由javac將類變量賦值動(dòng)作和靜態(tài)代碼塊中的語句合并自動(dòng)產(chǎn)生的。當(dāng)類不存在類變量和靜態(tài)代碼塊時(shí)不會(huì)自動(dòng)生成此方法。一個(gè)類只會(huì)被加載一次,()方法是同步加鎖的。 總結(jié)了這么多,你學(xué)會(huì)了嗎? |
|
|