讀古今文學網 > 編寫高質量代碼:改善Java程序的151個建議 > 建議104:使用forName動態加載類文件 >

建議104:使用forName動態加載類文件

動態加載(Dynamic Loading)是指在程序運行時加載需要的類庫文件,對Java程序來說,一般情況下,一個類文件在啟動時或首次初始化時會被加載到內存中,而反射則可以在運行時再決定是否要加載一個類,比如從Web上接收一個String參數作為類名,然後在JVM中加載並初始化,這就是動態加載,此動態加載通常是通過Class.forName(String)實現的,只是這個forName方法到底是什麼意思呢?

我們知道一個類文件只有在被加載到內存中後才可能生成實例對象,也就是說一個對象的生成必然會經過以下兩個步驟:

加載到內存中生成Class的實例對象。

通過new關鍵字生成實例對象。

如果我們使用的是import關鍵字產生的依賴包,JVM在啟動時會自動加載所有依賴包下的類文件,這沒有什麼問題,如果要動態加載類文件,就要使用forName方法了,但問題是我們為什麼要使用forName方法動態加載一個類文件呢?那是因為我們不知道生成的實例對象是什麼類型(如果知道就不用動態加載),而且方法和屬性都不可訪問呀。問題又來了:動態加載的意義在什麼地方呢?

意義在於:加載一個類即表示要初始化該類的static變量,特別是static代碼塊,在這裡我們可以做大量的工作,比如註冊自己,初始化環境等,這才是我們要重點關注的邏輯,例如如下代碼:


class Utils{

//靜態代碼塊

static{

System.out.println("Do Something");

}

}

public class Client{

public static void main(Stringargs)throws Exception{

//動態加載

Class.forName("Utils");

}

}


注意看Client類,我們並沒有對Utils做任何初始化,只是通過forName方法加載了Utils類,但是卻產生了一個"Do Something"的輸出,這就是因為Utils類被加載後,JVM會自動初始化其static變量和static代碼塊,這是類加載機制所決定的。

對於此種動態加載,最經典的應用就是數據庫驅動程序的加載片段,代碼如下:


//加載驅動

Class.forName("com.mysql.jdbc.Driver");

String url="jdbc:mysql://localhost:3306/db?user=&password=";

Connection conn=DriverManager.getConnection(url);

Statement stmt=conn.createStatement();

……


在沒有Hibernate和Ibatis等ORM框架的情況下,基本上每個系統都會有這麼一個JDBC連接類,然後提供諸如Query、Delete等的方法,大家有沒有想過為什麼要加上forName這句話呢?沒有任何的輸出呀,要它幹什麼用呢?事實上非常有用,我們看一下Driver類的源碼:


public class Driver extends NonRegisteringDriver implements java.sql.Driver{

//靜態代碼塊

static{

try{

//把自己註冊到DriverManager中

java.sql.DriverManager.registerDriver(new Driver());

}catch(SQLException E){

//異常處理

}

}

//構造函數

public Driver()throws SQLException{

}

}


該程序的邏輯是這樣的:數據庫的驅動程序已經由NonRegisteringDriver實現了,Driver類只是負責把自己註冊到DriverManager中。當程序動態加載該驅動時,也就是執行到Class.forName("com.mysql.jdbc.Driver")時,Driver類會被加載到內存中,於是static代碼塊開始執行,也就是把自己註冊到DriverManager中。

需要說明的是,forName只是把一個類加載到內存中,並不保證由此產生一個實例對象,也不會執行任何方法,之所以會初始化static代碼,那是由類加載機制所決定的,而不是forName方法決定的。也就是說,如果沒有static屬性或static代碼塊,forName就只是加載類,沒有任何的執行行為。

注意 forName只是加載類,並不執行任何代碼。