1. 程式人生 > >java 類載入器載入順序 經典例子

java 類載入器載入順序 經典例子

  寫了一個string 類,和api中包名,類名都是一樣的,然後去載入: 

1 package java.lang;
2 public class String{
3     public static void main(String[] args ){    
4     }
5 }

 大家發現什麼不同了嗎?對了,我們寫了一個與JDK中String一模一樣的類,連包java.lang都一樣,唯一不同的是我們自定義的String類有一個main函式。我們來執行一下:

 java.lang.NoSuchMethodError: main
 Exception in thread "main"

這是為什麼? 我們的String類不是明明有main方法嗎?

其實聯絡到jvm類載入的雙親委託模型,我們就能解釋這個問題了。

      執行這段程式碼,AppClassLoader會嘗試載入java.lang.String這個類,但是根據雙親委託模型AppClassLoader會將載入java.lang.String的請求委託給ExtClassLoader,而 ExtClassLoader又會委託給最後的啟動類載入器BootstrapLoader。

      啟動類載入器BootstrapLoader只能載入JAVA_HOME\jre\lib中的class類(即J2SE API),問題是標準API中確實有一個java.lang.String(注意,這個類和我們自定義的類是完全兩個類)。BootstrapLoader以為找到了這個類,毫不猶豫的載入了j2se api中的java.lang.String。

      最後出現上面的載入錯誤(注意不是異常,是錯誤,JVM退出),因為API中的String類是沒有main方法的。

結論:我們當然可以自定義一個和API完全一樣的類,但是由於雙親委託模型,使得我們不可能載入上我們自定義的這樣一個類。所以J2SE規範中希望我們自定義的包有自己唯一的特色(網路域名)。還有一點,這種載入器原理使得JVM更加安全的執行程式,因為黑客很難隨意的替代掉API中的程式碼了。

Java虛擬機器中可以安裝多個類載入器,系統預設三個主要類載入器,每個類負責載入特定位置的類:BootStrap,ExtClassLoader,AppClassLoader 類載入器也是Java類,因為其他是java類的類載入器本身也要被類載入器載入,顯然必須有第一個類載入器不是不是java類,這正是BootStrap。 jvm三級類載入器 類載入採用委託模型,就是頂層類載入器先是啟動,然後將載入類動作傳給(委託)上一層父類載入器,依次到bootstrap classloader,當根載入器沒有載入到,那麼又會返回給其下一級子類載入器,依次又傳給當前的子類載入器。如果還沒有找到,那麼就是class not found.說白了無論載入什麼類,都是從根載入起來的,根類庫沒有依次往後找。 為什麼要有這種委託機制? 這樣的好處,不讓你修改Java原生的類庫和本地類庫,如果你想自己定義,可以應該是修改了包名或者類名,這樣也能防止網路傳過來的類取代本地類庫或者api,防止入侵。