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会自动去加载合适的驱动。

相关文章
相关标签/搜索