類加載器 類加載的第一個階段就需要通過一個類的全限定名來獲取描述此類的二進(jìn)制字節(jié)流,實(shí)現(xiàn)這個動作的模塊就是類加載器。 類加載器雖然只是實(shí)現(xiàn)類的加載動作,但是在Java程序中的作用遠(yuǎn)不止于此。在Java中一個類的唯一性不僅僅是看類本身,還要看它的加載器。通俗地說:比較兩個類是否相等,只有在兩個類時由同一個類加載器加載的前提下才有意義,否則,即使兩個類來源于同一個Class文件,被同一個虛擬機(jī)加載,只要類加載器不同,兩個類也不相等。 下面看周志明老師那本書上例舉的一段代碼: import java.io.IOException; import java.io.InputStream; public class ClassLoaderTest { public static void main(String[] args) throws Exception{ ClassLoader myLoader = new ClassLoader() { @Override public Class<?> loadClass(String name) throws ClassNotFoundException { try { String fileName = name.substring(name.lastIndexOf('.') 1) '.class'; InputStream is = getClass().getResourceAsStream(fileName); if (is==null) { return super.loadClass(name); } byte[] b =new byte[is.available()]; is.read(b); return defineClass(name,b,0,b.length); } catch (IOException e) { throw new ClassNotFoundException(name); } } }; Object obj = myLoader.loadClass('ClassLoaderTest').newInstance(); System.out.println(obj.getClass()); //class ClassLoaderTest System.out.println(obj instanceof ClassLoaderTest); //false } }java學(xué)習(xí)交流群:478052716 注釋后打印了輸出內(nèi)容,我們使用自己定義的類加載器去加載本身產(chǎn)生的class文件,產(chǎn)生Class類,再實(shí)例化產(chǎn)生了obj對象。從打印的第一句看出,obj確實(shí)是ClassLoaderTest實(shí)例化的對象,但第二句返回了false,因?yàn)橄到y(tǒng)內(nèi)存在兩個ClassLoaderTest類,一個是系統(tǒng)應(yīng)用程序類加載器加載的,一個是我們自定義的類加載器加載的,雖然來自于同一個class文件,但是卻是兩個類。 上面我們提到了應(yīng)用程序加載器,自定義類加載器什么的,別急,繼續(xù)往下看。 雙親委派模型 什么是雙親委派模型想必也是面試中的常問考點(diǎn)。之前我們也提到過在JVM中,只存在兩種類加載器:
一般來說,在討論類加載器時,我們會劃分的更細(xì),我們可以看下圖: 類加載器 下面的討論都將基于這幅圖。 絕大部分的Java程序都會使用到以下3種系統(tǒng)提供的類加載器。
雙親委派模型要求除了頂層的
使用雙親委派模型最直接的好處就是Java類隨著它的類加載器一起具備了一種帶有優(yōu)先級的層次關(guān)系,例如 下面我們從ClassLoader的源碼來看看雙親委派模式: protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { // First, check if the class has already been loaded Class<?> c = findLoadedClass(name); //檢查該類是否加載過了 if (c == null) {//沒加載過的情況 long t0 = System.nanoTime(); try { if (parent != null) { //如果自定義的類加載器的parent不為null,就調(diào)用parent的loadClass進(jìn)行加載類 c = parent.loadClass(name, false); } else { //否則就去找bootstrap ClassLoader c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader } if (c == null) { // If still not found, then invoke findClass in order // to find the class. long t1 = System.nanoTime(); c = findClass(name); // this is the defining class loader; record the stats sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } } if (resolve) { resolveClass(c); } return c; } } 從上面的代碼來看,使用指定名稱加載類分為以下3步:
因?yàn)?code>loadClass封裝了雙親委派模型,所以在開發(fā)自己的類加載器時,Java標(biāo)準(zhǔn)提覆寫 通常情況下,Java虛擬機(jī)都是從文件系統(tǒng)里load一個class,但是有一些類不一定來自一個文件,它們也可能來自別的源,比如網(wǎng)絡(luò),加密文件等等,假設(shè)我們寫一個自己的類加載器,加載服務(wù)器下載的class文件。 示例使用代碼: ClassLoader loader = new NetworkClassLoader(host, port); Object main = loader.loadClass('Main', true).newInstance(); 在定義時,我們覆寫findClass方法: class NetworkClassLoader extends ClassLoader { String host; int port; public Class findClass(String name) { byte[] b = loadClassData(name); return defineClass(name, b, 0, b.length); } private byte[] loadClassData(String name) { // load the class data from the connection . . . } } 這里重要的還有個 |
|