標籤:
擷取一個資料庫連接的通用模板如下:
String driver = "oracle.jdbc.OracleDriver";String url = "jdbc:oracle:thin:@127.0.0.1:1521:orcl";String user = "scott";String password = "ticmy";Class.forName(driver);Connection conn = DriverManager.getConnection(url, user, password); |
裡面有個Class.forName(driver),這句話有什麼作用?將驅動類load到記憶體?如果沒有這句會怎麼樣?運行發現,如果去掉這一句會有以下異常:
java.sql.SQLException: No suitable driver found for xxx….
在解釋具體原因之前先簡單看下Class.forName做了什麼。
假設一個類以前從來沒有被裝進記憶體過,Class.forName(String className)這個方法會做以下幾件事情:
1、裝載。將位元組碼讀入記憶體,併產生一個與之對應的java.lang.Class類對象
2、串連。這一步會驗證位元組碼,為static變數分配記憶體,並賦預設值(0或null),並可選的解析符號引用(這裡不理解沒關係)
3、初始化。為類的static變數賦初始值,假如有static int a = 1;這個將a賦值為1的操作就是這個時候做的。除此之外,還要調用類的static塊。(這一步是要點)
Class.forName(String className)方法會將這三步都做掉,如下面的例子:
package com.ticmy.oracle; public class TestClinit { public static void main(String[] args) throws Exception { Class.forName("com.ticmy.oracle.ABC"); }}class ABC { private static int a = getNum(); static { System.out.println("this is static block"); } public static int getNum() { System.out.println("getNum"); return 1; }} |
程式的運行結果是:
getNum
this is static block
那麼,Class.forName(driver)這個driver類裡有沒有什麼static塊呢?去探究一下。例子用的是Oracle,反編譯下oracle.jdbc.OracleDriver,發現其繼承了oracle.jdbc.driver.OracleDriver,那麼繼續看這個oracle.jdbc.driver.OracleDriver,確實有個static塊,裡面有這樣的代碼:
static { Timestamp localTimestamp = Timestamp.valueOf("2000-01-01 00:00:00.0"); try { if (defaultDriver == null) { defaultDriver = new OracleDriver(); DriverManager.registerDriver(defaultDriver); } } catch (RuntimeException localRuntimeException) { } catch (SQLException localSQLException){} _Copyright_2004_Oracle_All_Rights_Reserved_ = null;} |
再看看mysql吧:com.mysql.jdbc.Driver:
同樣發現了static塊,裡面代碼如下:
static { try { DriverManager.registerDriver(new Driver()); } catch (SQLException E) { throw new RuntimeException("Can‘t register driver!"); }} |
再看一個db2:com.ibm.db2.jcc.DB2Driver:
也發現了static塊:
static { if (o.Nb != null) { exceptionsOnLoadDriver__ = dg.a(o.Nb, exceptionsOnLoadDriver__); } try { registeredDriver__ = new DB2Driver(); DriverManager.registerDriver(registeredDriver__); } catch (SQLException localSQLException) { exceptionsOnLoadDriver__ = new SqlException(null, "Error occurred while trying to register Jcc driver with JDBC 1 Driver Manager"); exceptionsOnLoadDriver__.setNextException(localSQLException); }} |
無一例外地,發現裡面都有DriverManager.registerDriver(driver)的調用。那麼是不是可以將開頭的例子中的Class.forName換成DriverManager.registerDriver呢?
String url = "jdbc:oracle:thin:@127.0.0.1:1521:orcl";String user = "scott";String password = "ticmy";DriverManager.registerDriver(new OracleDriver());Connection conn = DriverManager.getConnection(url, user, password);System.out.println(conn);conn.close(); |
經過測試發現OK。現在,已經知道Class.forName(driver)的根本目的就是為了調用DriverManager.registerDriver。
Class.forName還有個重載的方法:Class.forName(String name, boolean initialize, ClassLoader loader),Class.forName(String className)就等價於Class.forName(className, true, currentLoader),注意中間的參數為true,這個參數的含義就是要不要初始化。如果此參數為true且指定的類以前沒有被初始化過,就會去初始化。
另外,jdbc4已經不需要顯式的調用Class.forName了,在jdbc4中,調用getConnection的時候DriverManager會自動去載入合適的驅動。
http://www.ticmy.com/?p=249
JDBC在getConnection之前為什麼要調用Class.forName(轉)