Java加載類機制

類加載是Java程序運行的第一步,研究類的加載有助於瞭解JVM執行過程,並指導開發者採取更有效的措施配合程序執行。
研究類加載機制的第二個目的是讓程序能動態的控制類加載,比如熱部署等,提高程序的靈活性和適應性。
 
一、簡單過程
 
Java程序運行的場所是內存,當在命令行下執行:
java HelloWorld
命令的時候,JVM會將HelloWorld.class加載到內存中,並形成一個Class的對象HelloWorld.class。
其中的過程就是類加載過程:
1、尋找jre目錄,尋找jvm.dll,並初始化JVM;
2、產生一個Bootstrap Loader(啓動類加載器);
3、Bootstrap Loader自動加載Extended Loader(標準擴展類加載器),並將其父Loader設爲Bootstrap Loader。
4、Bootstrap Loader自動加載AppClass Loader(系統類加載器),並將其父Loader設爲Extended Loader。
5、最後由AppClass Loader加載HelloWorld類。
 
以上就是類加載的最一般的過程。
 
二、類加載器各自搜索的目錄
 
爲了弄清楚這個問題,首先還要看看System類的API doc文檔。
 
 
相關值的描述
java.version Java 運行時環境版本
java.vendor Java 運行時環境供應商
java.vendor.url Java 供應商的 URL
java.home Java 安裝目錄
java.vm.specification.version Java 虛擬機規範版本
java.vm.specification.vendor Java 虛擬機規範供應商
java.vm.specification.name Java 虛擬機規範名稱
java.vm.version Java 虛擬機實現版本
java.vm.vendor Java 虛擬機實現供應商
java.vm.name Java 虛擬機實現名稱
java.specification.version Java 運行時環境規範版本
java.specification.vendor Java 運行時環境規範供應商
java.specification.name Java 運行時環境規範名稱
java.class.version Java 類格式版本號
java.class.path Java 類路徑
java.library.path 加載庫時搜索的路徑列表
java.io.tmpdir 默認的臨時文件路徑
java.compiler 要使用的 JIT 編譯器的名稱
java.ext.dirs 一個或多個擴展目錄的路徑
os.name 操作系統的名稱
os.arch 操作系統的架構
os.version 操作系統的版本
file.separator 文件分隔符(在 UNIX 系統中是“/”)
path.separator 路徑分隔符(在 UNIX 系統中是“:”)
line.separator 行分隔符(在 UNIX 系統中是“/n”)
user.name 用戶的賬戶名稱
user.home 用戶的主目錄
user.dir 用戶的當前工作目錄
 
可惜這個幫助文檔並不全,直接用程序打印出來如下:
                for (Map.Entry<Object, Object> entry : System.getProperties().entrySet()) { 
                        System.out.println(entry.getKey()+"\t"+entry.getValue()); 
                }
 
java.runtime.name Java(TM) SE Runtime Environment
sun.boot.library.path Q:\jdk6\jre\bin
java.vm.version 14.0-b16
java.vm.vendor Sun Microsystems Inc.
java.vendor.url http://java.sun.com/
path.separator ;
idea.launcher.port 7532
java.vm.name Java HotSpot(TM) Client VM
file.encoding.pkg sun.io
sun.java.launcher SUN_STANDARD
user.country CN
sun.os.patch.level Service Pack 3
java.vm.specification.name Java Virtual Machine Specification
user.dir E:\projects\testScanner
java.runtime.version 1.6.0_14-b08
java.awt.graphicsenv sun.awt.Win32GraphicsEnvironment
java.endorsed.dirs Q:\jdk6\jre\lib\endorsed
os.arch x86
java.io.tmpdir C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\
line.separator
java.vm.specification.vendor Sun Microsystems Inc.
user.variant
os.name Windows XP
sun.jnu.encoding GBK
java.library.path Q:\jdk6\bin;.;C:\WINDOWS\Sun\Java\bin;C:\WINDOWS\system32;C:\WINDOWS;Q:\jdk6\bin;Q:\JavaFX\javafx-sdk1.2\bin;Q:\JavaFX\javafx-sdk1.2\emulator\bin;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\MySQL Server 5.1\bin;C:\Program Files\StormII\Codec;C:\Program Files\StormII
java.specification.name Java Platform API Specification
java.class.version 50
sun.management.compiler HotSpot Client Compiler
os.version 5.1
user.home d:\我的文檔
user.timezone
java.awt.printerjob sun.awt.windows.WPrinterJob
idea.launcher.bin.path C:\IDEA8\bin
file.encoding UTF-8
java.specification.version 1.6
java.class.path Q:\jdk6\jre\lib\alt-rt.jar;Q:\jdk6\jre\lib\charsets.jar;Q:\jdk6\jre\lib\deploy.jar;Q:\jdk6\jre\lib\javaws.jar;Q:\jdk6\jre\lib\jce.jar;Q:\jdk6\jre\lib\jsse.jar;Q:\jdk6\jre\lib\management-agent.jar;Q:\jdk6\jre\lib\plugin.jar;Q:\jdk6\jre\lib\resources.jar;Q:\jdk6\jre\lib\rt.jar;Q:\jdk6\jre\lib\ext\dnsns.jar;Q:\jdk6\jre\lib\ext\localedata.jar;Q:\jdk6\jre\lib\ext\sunjce_provider.jar;Q:\jdk6\jre\lib\ext\sunmscapi.jar;Q:\jdk6\jre\lib\ext\sunpkcs11.jar;E:\projects\testScanner\out\production\testScanner;C:\IDEA8\lib\idea_rt.jar
user.name Administrator
java.vm.specification.version 1
java.home Q:\jdk6\jre
sun.arch.data.model 32
user.language zh
java.specification.vendor Sun Microsystems Inc.
awt.toolkit sun.awt.windows.WToolkit
java.vm.info mixed mode, sharing
java.version 1.6.0_14
java.ext.dirs Q:\jdk6\jre\lib\ext;C:\WINDOWS\Sun\Java\lib\ext
sun.boot.class.path Q:\jdk6\jre\lib\resources.jar;Q:\jdk6\jre\lib\rt.jar;Q:\jdk6\jre\lib\sunrsasign.jar;Q:\jdk6\jre\lib\jsse.jar;Q:\jdk6\jre\lib\jce.jar;Q:\jdk6\jre\lib\charsets.jar;Q:\jdk6\jre\classes
java.vendor Sun Microsystems Inc.
file.separator \
java.vendor.url.bug http://java.sun.com/cgi-bin/bugreport.cgi
sun.io.unicode.encoding UnicodeLittle
sun.cpu.endian little
sun.desktop windows
sun.cpu.isalist
 
1、Bootstrap Loader(啓動類加載器):加載System.getProperty("sun.boot.class.path")所指定的路徑或jar。
2、Extended Loader(標準擴展類加載器ExtClassLoader):加載System.getProperty("java.ext.dirs")所指定的路徑或jar。在使用Java運行程序時,也可以指定其搜索路徑,例如:java -Djava.ext.dirs=d:\projects\testproj\classes HelloWorld
 
3、AppClass Loader(系統類加載器AppClassLoader):加載System.getProperty("java.class.path")所指定的路徑或jar。在使用Java運行程序時,也可以加上-cp來覆蓋原有的Classpath設置,例如: java -cp ./lavasoft/classes HelloWorld
 
ExtClassLoader和AppClassLoader在JVM啓動後,會在JVM中保存一份,並且在程序運行中無法改變其搜索路徑。如果想在運行時從其他搜索路徑加載類,就要產生新的類加載器。
 
三、類加載器的特點
 
1、運行一個程序時,總是由AppClass Loader(系統類加載器)開始加載指定的類。
2、在加載類時,每個類加載器會將加載任務上交給其父,如果其父找不到,再由自己去加載。
3、Bootstrap Loader(啓動類加載器)是最頂級的類加載器了,其父加載器爲null.
 
四、類加載器的獲取
 
很容易,看下面例子
public class HelloWorld { 
        public static void main(String[] args) { 
                HelloWorld hello = new HelloWorld(); 
                Class c = hello.getClass(); 
                ClassLoader loader = c.getClassLoader(); 
                System.out.println(loader); 
                System.out.println(loader.getParent()); 
                System.out.println(loader.getParent().getParent()); 
        } 
}
 
打印結果:
sun.misc.Launcher$AppClassLoader@19821f 
sun.misc.Launcher$ExtClassLoader@addbf1 
null 

Process finished with exit code 0
 
從上面的結果可以看出,並沒有獲取到ExtClassLoader的父Loader,原因是Bootstrap Loader(啓動類加載器)是用C語言實現的,找不到一個確定的返回父Loader的方式,於是就返回null。
 
五、類的加載
 
類加載有三種方式:
1、命令行啓動應用時候由JVM初始化加載
2、通過Class.forName()方法動態加載
3、通過ClassLoader.loadClass()方法動態加載
 
三種方式區別比較大,看個例子就明白了:
public class HelloWorld { 
        public static void main(String[] args) throws ClassNotFoundException { 
                ClassLoader loader = HelloWorld.class.getClassLoader(); 
                System.out.println(loader); 
                //使用ClassLoader.loadClass()來加載類,不會執行初始化塊 
                loader.loadClass("Test2"); 
                //使用Class.forName()來加載類,默認會執行初始化塊 
//                Class.forName("Test2"); 
                //使用Class.forName()來加載類,並指定ClassLoader,初始化時不執行靜態塊 
//                Class.forName("Test2", false, loader); 
        } 
}
 
public class Test2 { 
        static { 
                System.out.println("靜態初始化塊執行了!"); 
        } 
}
 
分別切換加載方式,會有不同的輸出結果。
 
六、自定義ClassLoader
 
爲了說明問題,先看例子:
package test; 

import java.net.MalformedURLException; 
import java.net.URL; 
import java.net.URLClassLoader; 

/** 
* 自定義ClassLoader 

* @author leizhimin 2009-7-29 22:05:48 
*/
 
public class MyClassLoader { 
        public static void main(String[] args) throws MalformedURLException, ClassNotFoundException, IllegalAccessException, InstantiationException { 
                URL url = new URL("file:/E:\\projects\\testScanner\\out\\production\\testScanner"); 
                ClassLoader myloader = new URLClassLoader(new URL[]{url}); 
                Class c = myloader.loadClass("test.Test3"); 
                System.out.println("----------"); 
                Test3 t3 = (Test3) c.newInstance(); 
        } 
}
 
public class Test3 { 
        static { 
                System.out.println("Test3的靜態初始化塊執行了!"); 
        } 
}
 
運行後:
---------- 
Test3的靜態初始化塊執行了! 

Process finished with exit code 0
 
可以看出自定義了ClassLoader myloader = new URLClassLoader(new URL[]{url});已經成功將類Test3加載到內存了,並通過默認構造方法構造了對象Test3 t3 = (Test3) c.newInstance();
 
有關ClassLoader還有很重要一點:
同一個ClassLoader加載的類文件,只有一個Class實例。但是,如果同一個類文件被不同的ClassLoader載入,則會有兩份不同的ClassLoader實例(前提是着兩個類加載器不能用相同的父類加載器)。




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章