java中的類是動態加載的,我們先看一下我們常用的類加載方式,先有一個感性的認識,才能進一步
深入討論,類加載無非就是下面三種方式。
class A{}
class B{}
class C{}
public class Loader{
??? public static void main(String[] args) throws Exception{
?????? Class aa=A.class;
?????? Class bb=Class.forName("B");
?????? Class cc=ClassLoader.getSystemClassLoader().loadClass("C");
??? }
}
我們先看.class字面量方式,很多人可能不知道這種方式,因為這種用法不是一般java語法。
通過javap我們可以發現,這種方式的大致等價于定義了一個靜態成員變量
??? static Class class$0;(后面的編號是增長的)
你可以試圖再定義一個? static Class class$0,應該會收到一個編譯錯誤(重復定義)。
深入討論,類加載無非就是下面三種方式。
class A{}
class B{}
class C{}
public class Loader{
??? public static void main(String[] args) throws Exception{
?????? Class aa=A.class;
?????? Class bb=Class.forName("B");
?????? Class cc=ClassLoader.getSystemClassLoader().loadClass("C");
??? }
}
我們先看.class字面量方式,很多人可能不知道這種方式,因為這種用法不是一般java語法。
通過javap我們可以發現,這種方式的大致等價于定義了一個靜態成員變量
??? static Class class$0;(后面的編號是增長的)
你可以試圖再定義一個? static Class class$0,應該會收到一個編譯錯誤(重復定義)。
Class aa=A.class;
就相當于
??? if(class$0==null){
?try{
?????????? Class.forName("A");
?}
?cacth(ClassNotFoundException e){
??? throw new NoClassDefFoundError(e);
?}
??? }
??? Class aa=class$0;
就相當于
??? if(class$0==null){
?try{
?????????? Class.forName("A");
?}
?cacth(ClassNotFoundException e){
??? throw new NoClassDefFoundError(e);
?}
??? }
??? Class aa=class$0;
可以很清楚的看到,這種類的字面量定義其實不是加載類的方式,而是被編譯器處理了,實質
上是使用了Class.forName方法,但是使用這種方式有一個很大的好處就是不用處理異常,因為
編譯器處理的時候如果找不到類會拋出一個NoClassDefFoundError。也許你覺得需要處理
ClassNotFoundException這種異常,事實上99%的情況下我們可以把這種異常認為是一個錯誤。
所以大部分情況我們使用這種方式會更簡潔。
上是使用了Class.forName方法,但是使用這種方式有一個很大的好處就是不用處理異常,因為
編譯器處理的時候如果找不到類會拋出一個NoClassDefFoundError。也許你覺得需要處理
ClassNotFoundException這種異常,事實上99%的情況下我們可以把這種異常認為是一個錯誤。
所以大部分情況我們使用這種方式會更簡潔。
最常用的方式就是Class.forName方式了,這也是一個通用的上層調用。這個方法有兩個重載,
可能很多人都忽略了第二個方法。
public static Class forName(String name) throws ClassNotFoundException
public static Class forName(String name, boolean initialize,ClassLoader loader) throws ClassNotFoundException
可能很多人都忽略了第二個方法。
public static Class forName(String name) throws ClassNotFoundException
public static Class forName(String name, boolean initialize,ClassLoader loader) throws ClassNotFoundException
第二個方法后面多了兩個參數,第二個參數表示是否初始化,第三個參數為指定的類加載器。
在上面的例子中:
Class bb=Class.forName("B");等價于
Class bb=Class.forName("B",true,Loader.class.getClassLoader());
這里要詳細說一下這個類的初始化這個參數,如果這個參數為false的話,
類中的static成員不會被初始化,static語句塊也不會被執行。
也就是類雖然被加載了,但是沒有被初始化,不過在第一次使用時仍然會初始化。
所以我們有時候會看到Class.forName("XXX").newInstance()這樣的語句,為什么這里要創建一個
不用的實例呢?不過是為了保證類被初始化(兼容以前的系統)。
其實第二個方法是比較難用的,需要指定類加載器,如果不指定而且又沒有安裝安全管理器的化,
是無法加載類的,只要看一下具體的實現就明白了。
在上面的例子中:
Class bb=Class.forName("B");等價于
Class bb=Class.forName("B",true,Loader.class.getClassLoader());
這里要詳細說一下這個類的初始化這個參數,如果這個參數為false的話,
類中的static成員不會被初始化,static語句塊也不會被執行。
也就是類雖然被加載了,但是沒有被初始化,不過在第一次使用時仍然會初始化。
所以我們有時候會看到Class.forName("XXX").newInstance()這樣的語句,為什么這里要創建一個
不用的實例呢?不過是為了保證類被初始化(兼容以前的系統)。
其實第二個方法是比較難用的,需要指定類加載器,如果不指定而且又沒有安裝安全管理器的化,
是無法加載類的,只要看一下具體的實現就明白了。
最本質的方式當然是直接使用ClassLoader加載了,所有的類最終都是通過ClassLoader加載的,
Class cc=ClassLoader.getSystemClassLoader().loadClass("C");
這里通過使用系統類加載器來加載某個類,很直接的方式,但是很遺憾的是通過這種方式加載類,
類是沒有被初始化的(也就是初始化被延遲到真正使用的時候).不過我們也可以借鑒上面的經驗,加載
后實例化一個對象Class cc=ClassLoader.getSystemClassLoader().loadClass("C").newInstance()。
這里使用了系統類加載器,也是最常用的類加載器,從classpath中尋找要加載的類。
java中默認有三種類加載器:引導類加載器,擴展類加載器,系統類加載器。
java中的類加載有著規范的層次結構,如果我們要了解類加載的過程,需要明確知道哪個類被誰
加載,某個類加載器加載了哪些類等等,就需要深入理解ClassLoader的本質。
以上只是類加載的表面的東西,我們還將討論深層次的東西。
Class cc=ClassLoader.getSystemClassLoader().loadClass("C");
這里通過使用系統類加載器來加載某個類,很直接的方式,但是很遺憾的是通過這種方式加載類,
類是沒有被初始化的(也就是初始化被延遲到真正使用的時候).不過我們也可以借鑒上面的經驗,加載
后實例化一個對象Class cc=ClassLoader.getSystemClassLoader().loadClass("C").newInstance()。
這里使用了系統類加載器,也是最常用的類加載器,從classpath中尋找要加載的類。
java中默認有三種類加載器:引導類加載器,擴展類加載器,系統類加載器。
java中的類加載有著規范的層次結構,如果我們要了解類加載的過程,需要明確知道哪個類被誰
加載,某個類加載器加載了哪些類等等,就需要深入理解ClassLoader的本質。
以上只是類加載的表面的東西,我們還將討論深層次的東西。
原文:http://dev.csdn.net/author/treeroot/a481eb323af84caab1149221432e46b9.html