Java中的類加載器

類加載器負責運行時(Runtime)把class文件動態的加載到虛擬機(JVM)中。同時,類加載器也是JRE的一部分,所以JRE可以不關心底層文件系統,就可以運行Java程序。此外,這些 Java 類不是一次性加載到內存中的,而是在應用程序需要時加載到內存中。

類加載器簡介

首先我們通過一段代碼來看一下Java中的類加載器:

public class ClassLoaderTest {

@Test

public void testClassLoaders() {

System.out.println("Classloader of this class:"

+ ClassLoaderTest.class.getClassLoader());

System.out.println("Classloader of Logging:"

+ Logging.class.getClassLoader());

System.out.println("Classloader of ArrayList:"

+ ArrayList.class.getClassLoader());

// 打印結果

// Classloader of this class:sun.misc.Launcher$AppClassLoader@18b4aac2

// Classloader of Logging:sun.misc.Launcher$ExtClassLoader@6325a3ee

// Classloader of ArrayList:null

}

}

從打印結果來看,包含3種類加載器:應用類加載器(Application ClassLoader), 擴展類加載器(Extension ClassLoader)和 一個啟動類加載器(展示位null)。

  • 應用類加載器
    在類路徑中加載我們自己的文件,它是擴展類加載器的子類。
  • 擴展類加載器加載的類是核心 Java 類的擴展,默認放在/lib/ext目錄中,或者被java.ext.dirs系統變量所指定的路徑中的所有類庫。它是啟動類加載器的子類。
  • 啟動類加載器是所有其他類的父類,默認放在/lib目錄中,或者-Xbootclasspath參數所指定的路徑中。是虛擬機識別的類庫,用戶無法直接使用。對於ArrayList為什麼打印出來的是null,那是因為啟動類加載器都是用本地方法(native)寫的,而不是Java,所以不會顯示類。

類加載器是如何工作的

上面我們已經知道,類加載器是JRE的一部分。當 JVM 嘗試加載一個類時,類加載器嘗試定位該類並使用完全限定的類名將類信息加載進入runtime。
java.lang.ClassLoader.loadClass()
負責將類加入runtime,如果runtime中沒有該類,會首先從父類加載,如果父類沒有加載,然後嘗試從子類加載,這就是雙親委派模型

。如果最後都沒有加載到該類,則會拋出ClassNotFoundException異常。

雙親委派模型: 當一個類加載器收到類加載請求時,它首先不會自己去加載這個類的信息,而是把該請求轉發給父類加載器,依次向上。所以所有的類加載請求都會被傳遞到父類加載器中,只有當父類加載器中無法加載到所需的類,子類加載器才會自己嘗試去加載該類。

在大部分情況下,內置的幾種類加載器已經夠用了,但是在需要從本地硬盤或網絡加載類的場景中,我們可能需要使用自定義類加載器。

public class CustomClassLoader extends ClassLoader {

@Override

public Class findClass(String name) {

byte[] b = loadClassFromFile(name);

return defineClass(name, b, 0, b.length);

}

private byte[] loadClassFromFile(String fileName) {

InputStream inputStream = getClass().getClassLoader().getResourceAsStream(

fileName.replace('.', File.separatorChar) + ".class");

byte[] buffer;

ByteArrayOutputStream byteStream = new ByteArrayOutputStream();

int nextValue = 0;

try {

while ( (nextValue = inputStream.read()) != -1 ) {

byteStream.write(nextValue);

}

} catch (IOException e) {

e.printStackTrace();

}

buffer = byteStream.toByteArray();

return buffer;

}

}

loadClass方法

public Class> loadClass(String name, boolean resolve) throws ClassNotFoundException {}

這裡需要注意的是loadClass的第二個參數,當需要解析類的時候需要將該值設置為true,然而,我們並不總是需要解析一個類。 如果我們只需要確定類是否存在,那麼 resolve 參數設置為 false。

如class.forName加載類時resolve就為true;而classLoader.loadClass時默認resolve為false。


分享到:


相關文章: