一個關於類載入器載入順序的經典例子,載入順序經典例子
問題: 如果自己定義一個java.lang.String並運行會出現什麼情況?
我們看看下面的代碼:
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中的代碼了。
參照:http://hxraid.iteye.com/blog/747625