打印Thread.currentThread().getContextClassLoader(),顯示如下:
sun.misc.Launcher$AppClassLoader@19821f這個(gè)加載器是系統(tǒng)類加載器。ClassLoader.getSystemResourceAsStream("com/config.xml")使用的就是系統(tǒng)類加載器定位資源的。
??? //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、引導(dǎo)(bootstrap)類加載器(用來加載java API類),例如加載java.lang.String類
??? 2、擴(kuò)展類加載器(就是sun.misc.Launcher$ExtClassLoader,用來加載jre\lib\ext目錄下的jar包)
??? 3、系統(tǒng)類加載器(就是sun.misc.Launcher$AppClassLoader,主要用來加載CLASSPATH設(shè)置目錄中的Class)
創(chuàng)建一個(gè)URLClassLoader,發(fā)現(xiàn)其父加載器(parent,注意不是父類)的類型為sun.misc.Launcher$AppClassLoader,而sun.misc.Launcher$AppClassLoader和sun.misc.Launcher$ExtClassLoader的父類都是URLClassLoader。AppClassLoader的父加載器是ExtClassLoader,ExtClassLoader的父加載器為null,即bootstrap類加載器。
類加載有個(gè)雙親委托模式,
AppClassLoader的父加載器是ExtClassLoader ,ExtClassLoader 的父加載器是bootstrap classloader,bootstrap 是C++寫的類加載器,會(huì)負(fù)責(zé)加載java核心類庫,就是jre/lib/rt.jar
ExtClassLoader會(huì)加載擴(kuò)展類庫,就是jre/lib/ext下的庫。
雙親委托模式就是子加載器會(huì)先委托父加載器加載,父加載器加載不了子加載器才加載,
這樣做避免了重復(fù)加載,也加強(qiáng)了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,原因是都是用系統(tǒng)類加載器AppClassLoader加載的
注意:
1,在類A中使用Class.forName加載類B,那么加載類A的類加載器將會(huì)用于加載類B,這樣兩個(gè)類的類加載器是同一個(gè)。
2,Class.forName("")和classLoader.load("")的區(qū)別主要是前者會(huì)做初始化,后者不會(huì)。見jdk注釋:
A?call?to?forName("X")?causes?the?class?named?X?to?be?initialized.? 自己分別用兩種方式裝載一個(gè)帶靜態(tài)代碼的類就知道了。jdbc需要通過Class.forName("")的方式來裝載JDBC驅(qū)動(dòng)程序(例如
Class.forName("com.mysql.jdbc.Driver"),之所以用Class.forName而沒有用
ClassLoader.load(),就是因?yàn)樾枰狫VM完成Driver的初始化工作,而不僅僅是裝載),然后通過一個(gè)統(tǒng)一的工廠類
Java.sql.DriverManager來取得數(shù)據(jù)庫連接,并執(zhí)行各種操作。
Class.forName("")不僅load?class而且還保證resolve這個(gè)class,包括常量池解析,類初始化。。。這樣JDBC驅(qū)動(dòng)使用這個(gè)方法,才能保證類里的靜態(tài)方法執(zhí)行,一般驅(qū)動(dòng)類的靜態(tài)方法會(huì)向DriverManager注冊(cè)自己,如果用classloader.load("")就不一定會(huì)resolve這個(gè)class,也就不能保證注冊(cè)驅(qū)動(dòng)類!看了com.mysql.jdbc.Driver類的源碼,靜態(tài)代碼就一句:java.sql.DriverManager.registerDriver(new com.mysql.jdbc.Driver())
3,
參考
1)java系統(tǒng)類加載器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是一個(gè)Java字節(jié)碼轉(zhuǎn)換工具。它能夠把用JDK5.0編譯的Java Class轉(zhuǎn)換成可運(yùn)行在JVM1.4