第一部分:Java虛擬機啓動時,關於類加載方面的一些動作
|
-
檢查這個類是否已經被加載進去了
-
如果還沒有加載,調用父對象加載該類
-
如果父對象無法加載,調用本對象的findClass()取得這個類。
因爲JVM自帶的ClassLoader只是懂得從本地文件系統加載標準的java class文件,如果編寫自己的ClassLoader,可以
- 在執行非置信代碼之前,自動驗證數字簽名
- 動態地創建符合用戶特定需要的定製化構建類
- 從特定的場所取得java class,例如數據庫和網絡。
當創建自己的ClassLoader時,需要繼承java.lang.ClassLoader或者它的子類。在實例化每個ClassLoader對 象時,需要指定一個父對象;如果沒有的話,系統自動指定ClassLoader.getSystemClassLoader()爲父對象。
第五部分: Class.forName()與ClassLoader.loadClass()的區別
Class clazz = Class.forName("XXX.XXX");
與
ClassLoader cl = Thread.currentThread().getContextClassLoader();
Class clazz = cl.loadClass("XXX.XXX");
都可以裝載一個類那麼他們的區別是什麼呢?
進一步研究Class.forName()是調用
Class.forName(name, initialize, loader); 也就是Class.forName("XXX.XXX"); 等同與 Class.forName("XXX.XXX", true, CALLCLASS.class.getClassLoader());
第二次參數表示裝載類的時候是否初始化該類, 即調用類的靜態塊的語句及初始化靜態成員變量。
Class clazz = cl.loadClass("XXX.XXX"); 沒有指定是否初始化的選項。只有執行clazz.newInstance();時才能夠初始化類。可以說 Class.forName("XXX.XXX", false, cl)執行過程是一致的。只是ClassLoader.loadClass()是更底 層的操作。
看一下JDBC驅動的裝載。
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection("jdbcurl");
當調用Class.forName("com.mysql.jdbc.Driver");是Driver已經被初始化並註冊到DriverManager中。MySQL Driver的代碼
public class Driver extends NonRegisteringDriver
implements java.sql.Driver
{
public Driver()
throws SQLException
{
}
static
{
try
{
DriverManager.registerDriver(new Driver());
}
catch(SQLException E)
{
throw new RuntimeException("Can't register driver!");
}
}
}
改修JDBC驅動的裝載
ClassLoader cl = Thread.currentThread().getContextClassLoader();
Class clazz = cl.loadClass("com.mysql.jdbc.Driver");
clazz.newInstance();
Connection conn = DriverManager.getConnection("jdbcurl");
同樣可以執行。
進一步說:
Class.forName是從指定的classloader中裝載類,如果沒有指定,也就是一個參數的時候,是從裝載當前對象實例所在的classloader中裝載類.
而ClassLoader的實例調用loadclass方法,是指從當前ClassLoader實例中調用類,而這個實例與裝載當前所在類實例的Classloader也許不是同一個.
舉個例子吧, 有A,B , C兩個ClassLoader , 當前運行的類D的實例是d(裝載它的是A) , 如果D中使用Class.forName那麼就是使用的ClassLoader就是A,當然,也可以指定爲B. 而如果D中代碼找到的ClassLoader實例是C,那麼就是用D來裝載所指定的類.
爲什麼要用不同的ClassLoader 裝載?
舉例來說:如果在Class被載入的過程中,你希望使用在自己的Class Loader來實現特定的操作,請使用ClassLoader方式。
貌似CGLib之類的bytecode generation框架很多地方會使用指定特殊ClassLoader的方式。
使用多個classloader的情況非常常見,比如說我們的app server,那麼都是這樣的. 在Web與EJB間, 他們的classLoader就是不同的,這樣做的目的就是爲了避免兩者間類裝載的相互干擾.
再舉個例子:
Static初始化區塊在什麼時候被調用的問題?
Public A{Static{System.out.println(“HaHaHa”);}}
Class.forName(“A”);
Class.forName(“A”,false, ClassLoader.getSystemClassLoader());
看看奧妙在哪裏?Java ClassLoader機制和原理又是如何?
程序示例:
public class A {
static { System.out.println("A`static is executed!");}
public A() {System.out.println("A`construct is executed!");}
public void show(){System.out.println("A`method is executed!");}
}
調用程序1:
Class c = Class.forName("A");
Method m = c.getMethod("show", new Class[0]);
System.out.println("A`test is executed!");
Object obj = c.newInstance();
m.invoke(obj, new Object[0]);
執行結果:
A`static is executed!
A`test is executed!
A`construct is executed!
A`method is executed!
調用程序2:
Class c = ClassLoader.getSystemClassLoader().loadClass("A");
System.out.println("A`test is executed!");
Method m = c.getMethod("show", new Class[0]);
Object obj = c.newInstance();
m.invoke(obj, new Object[0]);
執行結果:
A`test is executed!
A`static is executed!
A`construct is executed!
A`method is executed!
可見執行順序爲先執行 static{}塊中的代碼,然後執行構造函數,之後纔是方法的調用。
classloader的兩種載入方式:
1)pre-loading預先載入,載入基礎類
2)load-on-demand按需求載入
java動態載入class的兩種方式:
1)implic it隱式,即利用實例化才載入的特性來動態載入class
2)explic it顯式方式,又分兩種方式:
a)java.lang.Class的forName()方法 (上述調用程序1採用此方式載入)
b)java.lang.ClassLoader的loadClass()方法(上述調用程序2採用此方式載入)
static塊在什麼時候執行?
當調用forName(String)載入class時執行,(這個過程在類的所有父類中遞歸地調用)
如果調用ClassLoader.loadClass並不會執行.
forName(String,false,ClassLoader)時也不會執行.
如果載入Class時沒有執行static塊則在第一次實例化時執行.比如new ,Class.newInstance()操作
static塊僅執行一次