获取一个数据库连接的通用模板如下:
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会自动去加载合适的驱动。