JDBC在getConnection以前爲何要調用Class.forName

本文爲轉載學習java

原文連接:http://www.ticmy.com/?p=249mysql

獲取一個數據庫鏈接的通用模板以下:sql

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作了什麼。oracle

假設一個類之前歷來沒有被裝進內存過,Class.forName(String className)這個方法會作如下幾件事情:
一、裝載。將字節碼讀入內存,併產生一個與之對應的java.lang.Class類對象
二、鏈接。這一步會驗證字節碼,爲static變量分配內存,並賦默認值(0或null),並可選的解析符號引用(這裏不理解不要緊)
三、初始化。爲類的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 blockui

那麼,Class.forName(driver)這個driver類裏有沒有什麼static塊呢?去探究一下。例子用的是Oracle,反編譯下oracle.jdbc.OracleDriver,發現其繼承了oracle.jdbc.driver.OracleDriver,那麼繼續看這個oracle.jdbc.driver.OracleDriver,確實有個static塊,裏面有這樣的代碼:this

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塊,裏面代碼以下:url

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會自動去加載合適的驅動。

相關文章
相關標籤/搜索