類加載的三種方式:
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;
可以很清楚的看到,這種類的字面量定義其實不是加載類的方式,而是被編譯器處理了,實質上是使用了Class.forName方法,但是使用這種方式有一個很大的好處就是不用處理異常,因爲編譯器處理的時候如果找不到類會拋出一個NoClassDefFoundError.也許你覺得需要處理ClassNotFoundException這種異常,事實上99%的情況下我們可以把這種異常認爲是一個錯誤。
就相當於
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方式了,這也是一個通用的上層調用。這個方法有兩個重載,可能很多人都忽略了第二個方法。
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());
在上面的例子中:
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中默認有三種類加載器:引導類加載器,擴展類加載器,系統類加載器。