From Gossip@Openhome

Java Gossip: 簡介 ClassLoader

Java在需要使用類別的時候,才會將類別載入,Java的類別載入是由類別載入器(Class loader)來達到的,預設上,在程式啟動之後,主要會有三個類別載入器:Bootstrap Loader、ExtClassLoader與AppClassLoader

Bootstrap Loader是由C++撰寫而成,預設上它負責搜尋JRE所在目錄的classes或lib目錄下的.jar檔案中(例如rt.jar)是否有指定的類別並載入(實際上是由系統參數sun.boot.class.path指定);預設上ExtClassLoader負責搜尋JRE所在目錄的lib/ext 目錄下的classes或.jar中是否有指定的類別並載入(實際上是由系統參數java.ext.dirs指定);AppClassLoader則搜尋 Classpath中是否有指定的classes並載入(由系統參數java.class.path指定)。

Bootstrap Loader會在JVM啟動之後載入,之後它會載入ExtClassLoader並將ExtClassLoader的parent設為Bootstrap Loader,然後BootstrapLoader再載入AppClassLoader,並將AppClassLoader的parent設定為 ExtClassLoader。

在載入類別時,每個類別載入器會先將載入類別的任務交由其parent,如果parent找不到,才由自己負責載入,如果自己也找不到,就會丟出 NoClassDefFoundError

每一個類別被載入後,都會有一個Class的實例來代表它,每個Class的實例都會記得是哪個ClassLoader載入它的,可以由Class的getClassLoader()取得載入該類別的ClassLoader。

來撰寫一個簡單的程式測試類別載入器,首先寫一個測試類別:

  • ClassLoaderTest.java
public class ClassLoaderTest {
static {
System.out.println("類別被載入...");
}
}

將這個類別先放在Classpath的路徑下,然後撰寫以下的程式:
  • ClassPathDemo.java
public class ClassLoaderDemo {
public static void main(String[] args) {
ClassLoaderTest test = new ClassLoaderTest();

ClassLoader loader =
test.getClass().getClassLoader();

System.out.println(loader);
System.out.println(loader.getParent());
System.out.println(loader.getParent().getParent());
}
}

執行程式之後,會出現以下的訊息:   
類別被載入...
sun.misc.Launcher$AppClassLoader@82ba41
sun.misc.Launcher$ExtClassLoader@923e30
null


這表示ClassLoaderTest類別是由sun.misc.Launcher產生的AppClassLoader所載入,而 AppClassLoader的parent是ExtClassLoader(AppClassLoader與ExtClassLoader都是內部類 別),ExtClassLoader的parent則是Bootstrap Loader,最後一個顯示null並不是表示沒有ClassLoader,而是因為Bootstrap Loader是由C++撰寫而成,在Java中並沒有一個實際代表它的類別,因而沒有類別實例來表示Bootstrap Loader。

如果把ClassLoaderTest的class檔案放到JRE目錄下的lib/ext/classes下,並重新執行程式,您會看到以下的訊息:

類別被載入...
sun.misc.Launcher$ExtClassLoader@923e30
null
Exception in thread "main" java.lang.NullPointerException
        at ClassLoaderDemo.main(ClassLoaderDemo.java:9)


 
這次ClassLoaderTest是由ExtClassLoader找到了,由於Bootstrap Loader並沒有代表它的實例,所以是null,因而最後一行試圖再使用getParent()時會出現NullPointException。

如果您將ClassLoaderTest的class檔案移至JRE目錄下的classes目錄下,執行程式的話會出現以下的訊息:

類別被載入...
null
Exception in thread "main" java.lang.NullPointerException
        at ClassLoaderDemo.main(ClassLoaderDemo.java:8)


出現null訊息,表示這次ClassLoaderTest是由Bootstrap Loader載入的。

這邊只是對類別載入器作個簡單的描述,想更深入瞭解類別載入器,可參考這篇 深入類別載入器