1.Java中的所有類,必須被裝載到jvm中才能運作,這個裝載工作是由jvm中的類裝載器完成的,
類裝載器所做的工作實質是把類檔案從硬碟讀取到記憶體中
2.java中的類大緻分為三種:
1.系統類
2.擴充類
3.由程式員自定義的類
3.類裝載方式,有兩種
1.隐式裝載, 程式在運作過程中當碰到通過new
等方式生成對象時,隐式調用類裝載器加載對應的類到jvm中,
2.顯式裝載,
通過class.forname()等方法,顯式加載需要的類
隐式加載與顯式加載的差別:
兩者本質是一樣?,
?
4.類加載的動态性展現
一個應用程式總是由n多個類組成,Java程式啟動時,并不是一次把所有的類全部加載後再
運作,它總是先把保證程式運作的基礎類一次性加載到jvm中,其它類等到jvm用到的時候再加載,這樣的好處是節省了記憶體的開銷,因為java最早就是為嵌入式系統而設計的,記憶體寶貴,這是一種可以了解的機制,而用到時再加載這也是java動态性的一種展現
5.java類裝載器
Java中的類裝載器實質上也是類,功能是把類載入jvm中,值得注意的是jvm的類裝載器并不是一個,而是三個,層次結構如下:
Bootstrap Loader -
負責加載系統類
|
- -
ExtClassLoader -
負責加載擴充類
- - AppClassLoader -
負責加載應用類
為什麼要有三個類加載器,一方面是分工,各自負責各自的區塊,另一方面為了實作委托模型,下面會談到該模型
6. 類加載器之間是如何協調工作的
前面說了,java中有三個類加載器,問題就來了,碰到一個類需要加載時,它們之間是如何協調工作的,即java是如何區分一個類該由哪個類加載器來完成呢。
在這裡java采用了委托模型機制,這個機制簡單來講,就是“類裝載器有載入類的需求時,會先請示其Parent使用其搜尋路徑幫忙載入,如果Parent
找不到,那麼才由自己依照自己的搜尋路徑搜尋類”,注意喔,這句話具有遞歸性
下面舉一個例子來說明,為了更好的了解,先弄清楚幾行代碼:
Public
class Test{
Public static void main(String[]
arg){
ClassLoader c =
Test.class.getClassLoader();
//擷取Test類的類加載器
System.out.println(c);
ClassLoader
c1 = c.getParent();
//擷取c這個類加載器的父類加載器
System.out.println(c1);
ClassLoader c2 =
c1.getParent();//擷取c1這個類加載器的父類加載器
System.out.println(c2);
}
}
把以上代碼存到d:\my
檔案夾下,直接編譯,然後在dos模式下運作
D:\my\java Test
。。。AppClassLoader。。。
。。。ExtClassLoader。。。
Null
D:\my
注:
。。。表示省略了内容
可以看出Test是由AppClassLoader加載器加載的
AppClassLoader的Parent
加載器是 ExtClassLoader
但是ExtClassLoader的Parent為 null 是怎麼回事呵,朋友們留意的話,前面有提到Bootstrap
Loader是用C++語言寫的,依java的觀點來看,邏輯上并不存在Bootstrap
Loader的類實體,是以在java程式代碼裡試圖列印出其内容時,我們就會看到輸出為null
【注:以下内容大部分引用java深度曆險】
弄明白了上面的示例,接下來直接進入類裝載的委托模型執行個體,寫兩個檔案,如下:
檔案:Test1.java
class Test1{
System.out.println(Test1.class.getClassLoader());
Test2 t2 = new Test2();
T2.print();
}
檔案: Test2.java
Public class Test2{
Public void prin(){
System.out.println(this.getClass().getClassLoader());
這兩個類的作用就是列印出載入它們的類裝載器是誰, 将這兩個檔案儲存到d:\my目錄下,編譯後,我們在複制兩份,分别置于
<JRE所在目錄>\classes下(注意,剛開始我們的系統下沒有此目錄,需自己建立) 與
<JRE所在目錄>\lib\ext\classes下(同樣注意,開始我們的系統下也沒此目錄,手工建立),
然後切換到d:\my目錄下開始測試,
測試一:
<JRE所在目錄>\classes下
Test1.class
Test2.class
<JRE所在目錄>\lib\ext\classes下
D:\my下
dos下輸入運作指令,結果如下:
D:\my>java
Test1
Null
Null
D:\my>
從輸出結果我們可以看出,當AppClassLoader要載入Test1.class時,先請其Parent,也就是ExtClassLoader來載
入,而ExtclassLoader又請求其Parent,即Bootstrap Loader來載入Test1.class. 由于
<JRE所在目錄>\Classes目錄為Bootstrap Loader的搜尋路徑之一,是以Bootstrap
Loader找到了Test1.class,是以将它載入,接着在Test1.class之内有載入Test2.class的需求,由于
Test1.class是由Bootstrap Loader所載入,是以Test2.class内定是由Bootstrap
Loader根據其搜尋路徑來找,因Test2.class也位于Bootstrap
Loader可以找到的路徑下,是以也被載入了,最後我們看到Test1.class與Test2.class都是由Bootstrap
Loader(null)載入。
測試二:
Test1.class
Exception in thread “main”
java.lang.NoClassdefFoundError:Test2 at Test1.main。。。
D:\my>
Loader根據其搜尋路徑來找,但是因為Bootstrap Loader根本找不到Test2.class(被我們删除了),而Bootstrap
Loader又沒有Parent,是以無法載入Test2.class.最後我們看到Test1.class是由Bootstrap
Loader(null)載入,而Test2.class則無法載入
測試三
<JRE所在目錄>\classes下
入,而ExtclassLoader又請求其Parent,即Bootstrap Loader來載入Test1.class.但是Bootstrap
Loader無法在其搜尋路徑下找到Test1.class(被我們删掉了),是以ExtClassLoader隻得自己搜尋,是以
ExtClassLoader在其搜尋路徑
<JRE所在目錄>\lib\ext\classes下找到了Test1.class,是以将它載入,接着在Test1.class之内有載
入Test2.class的需求,由于Test1.class是由ExtClassLoader所載入,是以Test2.class内定是由
ExtClassLoader根據其搜尋路徑來找,但是因為ExtClassLoader有Parent,是以先由Bootstrap
Loader幫忙尋找,Test2.class位于Bootstrap Loader可以找到的路徑下,是以被Bootstrap
Loader載入了.最後我們看到Test1.class是由ExtClassLoader載入,而Test2.class則是由Bootstrap
Loader(null)載入
了解了以上規則,請朋友們自行分析以下場景的執行結果
測試四:
測試五:
測試六:
<JRE所在目錄>\lib\ext\classes下
測試七:
以上了解,錯漏之處請朋友們及時指出,以免怠誤大家