深刻理解java類加載

深刻研究Java類加載機制
 
類加載是Java程序運行的第一步,研究類的加載有助於瞭解JVM執行過程,並指導開發者採起更有效的措施配合程序執行。
研究類加載機制的第二個目的是讓程序能動態的控制類加載,好比熱部署等,提升程序的靈活性和適應性。
 
1、簡單過程
 
Java程序運行的場所是內存,當在命令行下執行:
java HelloWorld
命令的時候,JVM會將HelloWorld.class加載到內存中,並造成一個Class的對象HelloWorld.class。
其中的過程就是類加載過程:
一、尋找jre目錄,尋找jvm.dll,並初始化JVM;
二、產生一個Bootstrap Loader(啓動類加載器);
三、Bootstrap Loader自動加載Extended Loader(標準擴展類加載器),並將其父Loader設爲Bootstrap Loader。
四、Bootstrap Loader自動加載AppClass Loader(系統類加載器),並將其父Loader設爲Extended Loader。
五、最後由AppClass Loader加載HelloWorld類。
 
以上就是類加載的最通常的過程。
 
2、類加載器各自搜索的目錄
 
爲了弄清楚這個問題,首先還要看看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
 
一、Bootstrap Loader(啓動類加載器):加載System.getProperty("sun.boot.class.path")所指定的路徑或jar。
二、Extended Loader(標準擴展類加載器ExtClassLoader):加載System.getProperty("java.ext.dirs")所指定的 路徑或jar。在使用Java運行程序時,也能夠指定其搜索路徑,例如:java -Djava.ext.dirs=d:\projects\testproj\classes HelloWorld
 
三、AppClass Loader(系統類加載器AppClassLoader):加載System.getProperty("java.class.path")所指定的 路徑或jar。在使用Java運行程序時,也能夠加上-cp來覆蓋原有的Classpath設置,例如: java -cp ./lavasoft/classes HelloWorld
 
ExtClassLoader和AppClassLoader在JVM啓動後,會在JVM中保存一份,而且在程序運行中沒法改變其搜索路徑。若是想在運行時從其餘搜索路徑加載類,就要產生新的類加載器。
 
3、類加載器的特色
 
一、運行一個程序時,老是由AppClass Loader(系統類加載器)開始加載指定的類。
二、在加載類時,每一個類加載器會將加載任務上交給其父,若是其父找不到,再由本身去加載。
三、Bootstrap Loader(啓動類加載器)是最頂級的類加載器了,其父加載器爲null.
 
4、類加載器的獲取
 
很容易,看下面例子
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。
 
5、類的加載
 
類加載有三種方式:
一、命令行啓動應用時候由JVM初始化加載
二、經過Class.forName()方法動態加載
三、經過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("靜態初始化塊執行了!"); 
        } 
}
 
分別切換加載方式,會有不一樣的輸出結果。
 
6、自定義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實例(前提是着兩個類加載器不能用相同的父類加載器)。
相關文章
相關標籤/搜索