標籤:bsp board 系統變數 連結 assign 沒有 模式 exception cte
Java虛擬機器類載入過程是把Class類檔案載入到記憶體。並對Class檔案裡的資料進行校正、轉換解析和初始化,終於形成能夠被虛擬機器直接使用的java類型的過程。
在載入階段,java虛擬機器須要完畢下面3件事:
a.通過一個類的全限定名來擷取定義此類的二進位位元組流。
b.將定義類的二進位位元組流所代表的靜態儲存結構轉換為方法區的執行時資料結構。
c.在java堆中產生一個代表該類的java.lang.Class對象,作為方法區資料的訪問入口。
Java虛擬機器的類載入是通過類載入器實現的, Java中的類載入器體繫結構例如以下:
(1).BootStrap ClassLoader:啟動類載入器。負責載入存放在%JAVA_HOME%\lib檔案夾中的,或者通被-Xbootclasspath參數所指定的路徑中的。而且被java虛擬機器識別的(僅依照檔案名稱識別。如rt.jar,名字不符合的類庫,即使放在指定路徑中也不會被載入)類庫到虛擬機器的記憶體中,啟動類載入器無法被java程式直接引用。
(2).Extension ClassLoader:擴充類載入器,由sun.misc.Launcher$ExtClassLoader實現,負責載入%JAVA_HOME%\lib\ext檔案夾中的。或者被java.ext.dirs系統變數所指定的路徑中的全部類庫,開發人員能夠直接使用擴充類載入器。
(3).Application ClassLoader:應用程式類載入器,由sun.misc.Launcher$AppClassLoader實現。負責載入使用者類路徑classpath上所指定的類庫。是類載入器ClassLoader中的getSystemClassLoader()方法的傳回值,開發人員能夠直接使用應用程式類載入器,假設程式中沒有自己定義過類載入器,該載入器就是程式中預設的類載入器。
注意:上述三個JDK提供的類載入器儘管是父子類載入器關係,可是沒有使用繼承,而是使用了組合關係。
從JDK1.2開始。java虛擬機器規範推薦開發人員使用雙親委派模式(ParentsDelegation Model)進行類載入,其載入步驟例如以下:
(1).假設一個類載入器收到了類載入請求,它首先不會自己去嘗試載入這個類,而是把類載入請求委派給父類載入器去完畢。
(2).每一層的類載入器都把類載入請求委派給父類載入器,直到全部的類載入請求都應該傳遞給頂層的啟動類載入器。
(3).假設頂層的啟動類載入器無法完畢載入請求,子類載入器嘗試去載入,假設連最初發起類載入請求的類載入器也無法完畢載入請求時,將會拋出ClassNotFoundException。而不再調用其子類載入器去進行類載入。
雙親委派 模式的類載入機制的長處是java類它的類載入器一起具備了一種帶優先順序的層次關係。越是基礎的類。越是被上層的類載入器進行載入,保證了java程式的穩定執行。雙親委派模式的實現:
[java] view plaincopyprint?
- protected synchronized Class<?
> loadClass(String name, Boolean resolve) throws ClassNotFoundException{
- //首先檢查請求的類是否已經被載入過
- Class c = findLoadedClass(name);
- if(c == null){
- try{
- if(parent != null){//委派父類載入器載入
- c = parent.loadClass(name, false);
- }
- else{//委派啟動類載入器載入
- c = findBootstrapClassOrNull(name);
- }
- }catch(ClassNotFoundException e){
- //父類載入器無法完畢類載入請求
- }
- if(c == null){//本身類載入器進行類載入
- c = findClass(name);
- }
- }
- if(resolve){
- resolveClass(c);
- }
- return c;
- }
protected synchronized Class<?> loadClass(String name, Boolean resolve) throws ClassNotFoundException{//首先檢查請求的類是否已經被載入過Class c = findLoadedClass(name);if(c == null){try{if(parent != null){//委派父類載入器載入c = parent.loadClass(name, false);}else{//委派啟動類載入器載入c = findBootstrapClassOrNull(name); }}catch(ClassNotFoundException e){//父類載入器無法完畢類載入請求}if(c == null){//本身類載入器進行類載入c = findClass(name);}}if(resolve){resolveClass(c);}return c;}
若要實現自己定義類載入器,僅僅須要繼承java.lang.ClassLoader 類。而且重寫其findClass()方法就可以。java.lang.ClassLoader 類的基本職責就是依據一個指定的類的名稱,找到或者產生其相應的位元組代碼,然後從這些位元組代碼中定義出一個 Java 類,即 java.lang.Class 類的一個執行個體。除此之外,ClassLoader 還負責載入 Java 應用所需的資源,像檔案和設定檔等。ClassLoader 中與載入類相關的方法例如以下:
方法 |
說明 |
getParent() |
返回該類載入器的父類載入器。 |
loadClass(String name) |
載入名稱為 二進位名稱為name 的類。返回的結果是 java.lang.Class 類的執行個體。 |
findClass(String name) |
尋找名稱為 name 的類,返回的結果是 java.lang.Class 類的執行個體。 |
findLoadedClass(String name) |
尋找名稱為 name 的已經被載入過的類。返回的結果是 java.lang.Class 類的執行個體。 |
resolveClass(Class<?> c) |
連結指定的 Java 類。 |
注意:在JDK1.2之前。類載入尚未引入雙親委派模式,因此實現自己定義類載入器時經常重寫loadClass方法,提供雙親委派邏輯。從JDK1.2之後,雙親委派模式已經被引入到類載入體系中。自己定義類載入器時不須要在自己寫雙親委派的邏輯,因此不鼓舞重寫loadClass方法,而推薦重寫findClass方法。
在Java中。隨意一個類都須要由載入它的類載入器和這個類本身一同確定其在java虛擬機器中的唯一性,即比較兩個類是否相等。僅僅有在這兩個類是由同一個類載入器載入的前提之下才有意義,否則,即使這兩個類來源於同一個Class類檔案,僅僅要載入它的類載入器不同樣,那麼這兩個類必然不相等(這裡的相等包含代表類的Class對象的equals()方法、isAssignableFrom()方法、isInstance()方法和instanceofkeyword的結果)。
範例代碼例如以下:
[java] view plaincopyprint?
- package com.test;
-
- public class ClassLoaderTest {
- public static void main(String[] args)throws Exception{
- //匿名內部類實現自己定義類載入器
- ClassLoader myClassLoader = new ClassLoader(){
- protected Class<?> findClass(String name)throws ClassNotFoundException{
- //擷取類檔案名稱
- String filename = name.substring(name.lastIndexOf(“.”) + 1) + “.class”;
- InputStream in = getClass().getResourceAsStream(filename);
- if(in == null){
- throw RuntimeException(“Could not found class file:” + filename);
- }
- byte[] b = new byte[in.available()];
- return defineClass(name, b, 0, b.length);
- }catch(IOException e){
- throw new ClassNotFoundException(name);
- }
- };
- Object obj = myClassLoader.loadClass(“com.test.ClassLoaderTest”).newInstance();
- System.out.println(obj.getClass());
- System.out.println(obj instanceof com.test. ClassLoaderTest);
- }
- }
package com.test;public class ClassLoaderTest {public static void main(String[] args)throws Exception{//匿名內部類實現自己定義類載入器ClassLoader myClassLoader = new ClassLoader(){protected Class<?> findClass(String name)throws ClassNotFoundException{//擷取類檔案名稱String filename = name.substring(name.lastIndexOf(“.”) + 1) + “.class”;InputStream in = getClass().getResourceAsStream(filename);if(in == null){throw RuntimeException(“Could not found class file:” + filename);}byte[] b = new byte[in.available()];return defineClass(name, b, 0, b.length);}catch(IOException e){throw new ClassNotFoundException(name);}};Object obj = myClassLoader.loadClass(“com.test.ClassLoaderTest”).newInstance();System.out.println(obj.getClass());System.out.println(obj instanceof com.test. ClassLoaderTest);}}
輸出結果例如以下:
com.test.ClassLoaderTest
false
之所以instanceof會返回false。是由於com.test.ClassLoaderTest類預設使用Application ClassLoader載入,而obj是通過自己定義類載入器載入的。類載入不同樣,因此不相等。
類載入器雙親委派模型是從JDK1.2以後引入的。而且僅僅是一種推薦的模型。不是強制要求的,因此有一些沒有遵循雙親委派模型的特例:
(1).在JDK1.2之前,自己定義類載入器都要覆蓋loadClass方法去實現載入類的功能,JDK1.2引入雙親委派模型之後,loadClass方法用於委派父類載入器進行類載入。僅僅有父類載入器無法完畢類載入請求時才調用自己的findClass方法進行類載入,因此在JDK1.2之前的類載入的loadClass方法沒有遵循雙親委派模型。因此在JDK1.2之後。自己定義類載入器不推薦覆蓋loadClass方法。而僅僅須要覆蓋findClass方法就可以。
(2).雙親委派模式非常好地攻克了各個類載入器的基礎類統一問題,越基礎的類由越上層的類載入器進行載入,可是這個基礎類統一有一個不足,當基礎類想要調用回下層的使用者代碼時無法委派子類載入器進行類載入。為瞭解決問題JDK引入了ThreadContext線程上下文,通過線程內容相關的setContextClassLoader方法能夠設定線程上下文類載入器。
JavaEE僅僅是一個規範,sun公司僅僅給出了介面規範,詳細的實現由各個廠商進行實現,因此JNDI。JDBC,JAXB等這些第三方的實現庫就能夠被JDK的類庫所調用。
線程上下文類載入器也沒有遵循雙親委派模型。
(3).近年來的熱碼替換,模組熱部署等應用要求不用重新啟動java虛擬機器就能夠實現代碼模組的隨插即用。催生了OSGi技術。在OSGi中類載入器體系被發展為網狀結構。OSGi也沒有全然遵循雙親委派模型。
Java虛擬機器的類載入機制