打印Thread.currentThread().getContextClassLoader(),顯示如下:
sun.misc.Launcher$AppClassLoader@19821f這個加載器是系統類加載器。ClassLoader.getSystemResourceAsStream("com/config.xml")使用的就是系統類加載器定位資源的。
??? //JDK1.6,java.lang.ClassLoader的loadClass(String?name,?boolean?resolve)方法的源碼
protected?synchronized?Class<?>?loadClass(String?name,?boolean?resolve)
????throws?ClassNotFoundException
????{
????//?First,?check?if?the?class?has?already?been?loaded
????Class?c?=?findLoadedClass(name);
????if?(c?==?null)?{
????????try?{
????????if?(parent?!=?null)?{
?????????? //?
????????????c?=?parent.loadClass(name,?false);
????????}?else?{
?????????? //
????????????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.
??????????? //
????????????c?=?findClass(name);
????????}
????}
????if?(resolve)?{
????????resolveClass(c);
????}
????return?c;
????}
java中共有三種類型的類加載器:
??? 1、引導(bootstrap)類加載器(用來加載java API類),例如加載java.lang.String類
??? 2、擴展類加載器(就是sun.misc.Launcher$ExtClassLoader,用來加載jre\lib\ext目錄下的jar包)
??? 3、系統類加載器(就是sun.misc.Launcher$AppClassLoader,主要用來加載CLASSPATH設置目錄中的Class)
創建一個URLClassLoader,發現其父加載器(parent,注意不是父類)的類型為sun.misc.Launcher$AppClassLoader,而sun.misc.Launcher$AppClassLoader和sun.misc.Launcher$ExtClassLoader的父類都是URLClassLoader。AppClassLoader的父加載器是ExtClassLoader,ExtClassLoader的父加載器為null,即bootstrap類加載器。
類加載有個雙親委托模式,
AppClassLoader的父加載器是ExtClassLoader ,ExtClassLoader 的父加載器是bootstrap classloader,bootstrap 是C++寫的類加載器,會負責加載java核心類庫,就是jre/lib/rt.jar
ExtClassLoader會加載擴展類庫,就是jre/lib/ext下的庫。
雙親委托模式就是子加載器會先委托父加載器加載,父加載器加載不了子加載器才加載,
這樣做避免了重復加載,也加強了java的安全了,防止了惡意加載器去加載核心庫。
String?name?=?"com.domain.Account";
????????????
????????????URL?url1?=?new?URL("file:/D:/workspace/test/bin/");
????????????ClassLoader?cl?=?new?URLClassLoader(new?URL[]?{?url1?});
????????????Class?c1?=?cl.loadClass(name);
????????????
????????????URL?url2?=?new?URL("file:/D:/workspace/test/bin");
????????????ClassLoader?cl2?=?new?URLClassLoader(new?URL[]?{?url2?});
????????????Class?c2?=?cl2.loadClass(name);
????????????
????????????System.out.println(c1==c2);//返回true,原因是都是用系統類加載器AppClassLoader加載的
注意:
1,在類A中使用Class.forName加載類B,那么加載類A的類加載器將會用于加載類B,這樣兩個類的類加載器是同一個。
2,Class.forName("")和classLoader.load("")的區別主要是前者會做初始化,后者不會。見jdk注釋:
A?call?to?forName("X")?causes?the?class?named?X?to?be?initialized.? 自己分別用兩種方式裝載一個帶靜態代碼的類就知道了。jdbc需要通過Class.forName("")的方式來裝載JDBC驅動程序(例如
Class.forName("com.mysql.jdbc.Driver"),之所以用Class.forName而沒有用
ClassLoader.load(),就是因為需要JVM完成Driver的初始化工作,而不僅僅是裝載),然后通過一個統一的工廠類
Java.sql.DriverManager來取得數據庫連接,并執行各種操作。
Class.forName("")不僅load?class而且還保證resolve這個class,包括常量池解析,類初始化。。。這樣JDBC驅動使用這個方法,才能保證類里的靜態方法執行,一般驅動類的靜態方法會向DriverManager注冊自己,如果用classloader.load("")就不一定會resolve這個class,也就不能保證注冊驅動類!看了com.mysql.jdbc.Driver類的源碼,靜態代碼就一句:java.sql.DriverManager.registerDriver(new com.mysql.jdbc.Driver())
3,
參考
1)java系統類加載器AppClassLoader之淺談 http://blog.sina.com.cn/s/blog_4db6a3f101000do1.html
2)java類加載原理分析 http://gongmingwind.javaeye.com/blog/338366
3)解讀ClassLoader http://www.javaeye.com/topic/83978
4)http://xyiyy.javaeye.com/blog/362107
Retrotranslator是一個Java字節碼轉換工具。它能夠把用JDK5.0編譯的Java Class轉換成可運行在JVM1.4