Java類加載器的作用就是在運行時加載類。Java類加載器基于三個機制:委托、可見性和單一性。委托機制是指將加載一個類的請求交給父類加載器,如果這個父類加載器不能夠找到或者加載這個類,那么再加載它。可見性的原理是子類的加載器可以看見所有的父類加載器加載的類,而父類加載器看不到子類加載器加載的類。單一性原理是指僅加載一個類一次,這是由委托機制確保子類加載器不會再次加載父類加載器加載過的類。正確理解類加載器能夠幫你解決NoClassDefFoundError和java.lang.ClassNotFoundException,因為它們和類的加載相關。類加載器通常也是比較高級的Java面試中的重要考題,Java類加載器和工作原理以及classpath如何運作的經(jīng)常被問到。Java面試題中也經(jīng)常出現(xiàn)“一個類是否能被兩個不同類加載器加載”這樣的問題。這篇教程中,我們將學到類加載器是什么,它的工作原理以及一些關于類加載器的知識點。 什么是類加載器類加載器是一個用來加載類文件的類。Java源代碼通過javac編譯器編譯成類文件。然后JVM來執(zhí)行類文件中的字節(jié)碼來執(zhí)行程序。類加載器負責加載文件系統(tǒng)、網(wǎng)絡或其他來源的類文件。有三種默認使用的類加載器:Bootstrap類加載器、Extension類加載器和System類加載器(或者叫作Application類加載器)。每種類加載器都有設定好從哪里加載類。
除了Bootstrap類加載器是大部分由C來寫的,其他的類加載器都是通過java.lang.ClassLoader來實現(xiàn)的。 總結一下,下面是三種類加載器加載類文件的地方: 1) Bootstrap類加載器 – JRE/lib/rt.jar 2) Extension類加載器 – JRE/lib/ext或者java.ext.dirs指向的目錄 3) Application類加載器 – CLASSPATH環(huán)境變量, 由-classpath或-cp選項定義,或者是JAR中的Manifest的classpath屬性定義. 類加載器的工作原理我之前已經(jīng)提到過了,類加載器的工作原理基于三個機制:委托、可見性和單一性。這一節(jié),我們來詳細看看這些規(guī)則,并用一個實例來理解工作原理。下面顯示的是類加載器使用委托機制的工作原理。 委托機制當一個類加載和初始化的時候,類僅在有需要加載的時候被加載。假設你有一個應用需要的類叫作Abc.class,首先加載這個類的請求由Application類加載器委托給它的父類加載器Extension類加載器,然后再委托給Bootstrap類加載器。Bootstrap類加載器會先看看rt.jar中有沒有這個類,因為并沒有這個類,所以這個請求由回到Extension類加載器,它會查看jre/lib/ext目錄下有沒有這個類,如果這個類被Extension類加載器找到了,那么它將被加載,而Application類加載器不會加載這個類;而如果這個類沒有被Extension類加載器找到,那么再由Application類加載器從classpath中尋找。記住classpath定義的是類文件的加載目錄,而PATH是定義的是可執(zhí)行程序如javac,java等的執(zhí)行路徑。 可見性機制根據(jù)可見性機制,子類加載器可以看到父類加載器加載的類,而反之則不行。所以下面的例子中,當Abc.class已經(jīng)被Application類加載器加載過了,然后如果想要使用Extension類加載器加載這個類,將會拋出java.lang.ClassNotFoundException異常。
輸出:
單一性機制根據(jù)這個機制,父加載器加載過的類不能被子加載器加載第二次。雖然重寫違反委托和單一性機制的類加載器是可能的,但這樣做并不可取。你寫自己的類加載器的時候應該嚴格遵守這三條機制。 如何顯式的加載類Java提供了顯式加載類的API:Class.forName(classname)和Class.forName(classname, initialized, classloader)。就像上面的例子中,你可以指定類加載器的名稱以及要加載的類的名稱。類的加載是通過調(diào)用java.lang.ClassLoader的loadClass()方法,而loadClass()方法則調(diào)用了findClass()方法來定位相應類的字節(jié)碼。在這個例子中Extension類加載器使用了java.net.URLClassLoader,它從JAR和目錄中進行查找類文件,所有以”/”結尾的查找路徑被認為是目錄。如果findClass()沒有找到那么它會拋出java.lang.ClassNotFoundException異常,而如果找到的話則會調(diào)用defineClass()將字節(jié)碼轉(zhuǎn)化成類實例,然后返回。 什么地方使用類加載器類加載器是個很強大的概念,很多地方被運用。最經(jīng)典的例子就是AppletClassLoader,它被用來加載Applet使用的類,而Applets大部分是在網(wǎng)上使用,而非本地的操作系統(tǒng)使用。使用不同的類加載器,你可以從不同的源地址加載同一個類,它們被視為不同的類。J2EE使用多個類加載器加載不同地方的類,例如WAR文件由Web-app類加載器加載,而EJB-JAR中的類由另外的類加載器加載。有些服務器也支持熱部署,這也由類加載器實現(xiàn)。你也可以使用類加載器來加載數(shù)據(jù)庫或者其他持久層的數(shù)據(jù)。 以上是關于類加載器的工作原理。我們已經(jīng)知道了委托、可見性以及單一性原理,這些對于調(diào)試類加載器相關問題時至關重要。這些對于Java程序員和架構師來說都是必不可少的知識。 原文鏈接: Javarevisited 翻譯: ImportNew.com - 唐小娟譯文鏈接: http://www./6581.html [ 轉(zhuǎn)載請保留原文出處、譯者和譯文鏈接。] |
|