加載數據庫驅動的時候,有如下部分代碼:
1 //1) 注冊驅動程序 2 //給java.sql.Driver接口的引用賦值 com.mysql.jdbc.Driver 實現類對象 3 Driver driver = new com.mysql.jdbc.Driver(); 4 DriverManager.registerDriver(driver); //注冊驅動
先到數據庫驅動包看下這個Driver類:

可以看到這個類里面有個static語句塊,這里先來復習以下,什么時候會加載static語句塊,有如下情況:
當一個類被主動使用時,Java虛擬就會對其初始化,類初始化的時候,就會執行靜態代碼。如下六種情況為主動使用:
-
當創建某個類的新實例時(如通過new或者反射,克隆,反序列化等)
-
當調用某個類的靜態方法時
-
當使用某個類或接口的靜態字段時
-
當調用Java API中的某些反射方法時,比如類Class中的方法,或者java.lang.reflect中的類的方法時
-
當初始化某個子類時
-
當虛擬機啟動某個被標明為啟動類的類(即包含main方法的那個類)
Java編譯器會收集所有的類變量初始化語句和類型的靜態初始化器,將這些放到一個特殊的方法中:clinit。
特別要注意的是:使用final修飾的靜態字段,在調用的時候不會對類進行初始化!以及類被加載了不一定就會執行靜態代碼塊,只有一個類被主動使用的時候,靜態代碼才會被執行!
那么回到原來的問題,在創建Driver這個對象的時候:
Driver driver = new com.mysql.jdbc.Driver();
必然會執行其內的static語句塊的,其static語句塊的作用就和:
DriverManager.registerDriver(driver);
這段代碼的作用是一樣的,所以有必要再寫一次這行代碼嗎?
我試着刪除了這行代碼,發現還是能順利連接到數據庫。
查找了一些資料,找到了計算driver數量的方法,如下所示:
1 public class jbdctest01 { 2 public static void main(String[] args) throws ClassNotFoundException, SQLException { 3 //1) 注冊驅動程序 4 //給java.sql.Driver接口的引用賦值 com.mysql.jdbc.Driver 實現類對象 5 // Driver driver = new com.mysql.jdbc.Driver(); 6 // DriverManager.registerDriver(driver); //注冊驅動 7 //注冊驅動也可以使用Class.forName()創建Class對象時,會執行類的靜態代碼塊, 在com.mysql.jdbc.Driver類的靜態代碼塊中,注冊驅動 8 Class.forName("com.mysql.jdbc.Driver"); 9 //2)在當前程序與 數據庫服務器建立連接 10 // Connection connection = DriverManager.getConnection("jdbc:mysql://數據庫服務器IP地址:MySQL服務的端口號/數據庫名", "登錄服務器的用戶名", "登錄密碼"); 11 Enumeration<Driver> drivers = DriverManager.getDrivers(); 12 13 int nums = 0; 14 //使用while循環來遍歷驅動的個數 15 while(drivers.hasMoreElements()) { 16 nums ++; 17 //打印出驅動 18 System.out.println(drivers.nextElement()); 19 } 20 //打印出驅動個數 21 System.out.println("驅動個數:" + nums); 22 } 23 }
測試結果如圖所示:

用反射的方法加載驅動,驅動數為一個,這是意料之中的事情,在創建Driver這個對象時,會執行static中的代碼塊,因此會加載驅動,且數量為1
接下來測試,另外一種方法,代碼如下:
1 public class jbdctest01 { 2 public static void main(String[] args) throws ClassNotFoundException, SQLException { 3 //1) 注冊驅動程序 4 //給java.sql.Driver接口的引用賦值 com.mysql.jdbc.Driver 實現類對象 5 Driver driver = new com.mysql.jdbc.Driver(); 6 DriverManager.registerDriver(driver); //注冊驅動 7 //注冊驅動也可以使用Class.forName()創建Class對象時,會執行類的靜態代碼塊, 在com.mysql.jdbc.Driver類的靜態代碼塊中,注冊驅動 8 //Class.forName("com.mysql.jdbc.Driver"); 9 //2)在當前程序與 數據庫服務器建立連接 10 // Connection connection = DriverManager.getConnection("jdbc:mysql://數據庫服務器IP地址:MySQL服務的端口號/數據庫名", "登錄服務器的用戶名", "登錄密碼"); 11 Enumeration<Driver> drivers = DriverManager.getDrivers(); 12 13 int nums = 0; 14 //使用while循環來遍歷驅動的個數 15 while(drivers.hasMoreElements()) { 16 nums ++; 17 //打印出驅動 18 System.out.println(drivers.nextElement()); 19 } 20 //打印出驅動個數 21 System.out.println("驅動個數:" + nums); 22 } 23 }
測試結果如圖所示:

可以看到結果是2
接下來注釋掉:DriverManager.registerDriver(driver); 這行代碼,可以看到輸出結果:

可以看到驅動個數為1個,說明了DriverManager.registerDriver(driver);這行代碼可以去掉,如果加上,就會出現了兩個驅動,因為在創建Driver這個對象的時候,其內的static語句塊會自動執行了。
另外還有一個小的疑問,就是一個對象執行的時候,是先創建類還是先執行其內的靜態方法呢?我特意寫了一個程序試了一下:
1 public class Teststatic { 2 public Teststatic(){ 3 System.out.println("看看誰先被執行"); 4 5 } 6 static { 7 System.out.println("我被執行了,哈哈哈"); 8 } 9 } 10 class T { 11 public static void main(String[] args) { 12 Teststatic a = new Teststatic(); 13 14 } 15 }
運行結果如下:

說明static語句塊比構造方法執行的要早,我記得也是這樣的。那么,我就有了一個疑問,因為Driver中的static,要用到Driver的實現類,如果先執行static語句塊的話,不會拋出異常嗎?因為這個時候Driver的實現類還沒創建好啊
這里我犯了一個錯誤,就是誤認為構造方法執行完畢之后類才被創建,其實不是的,執行構造方法只是為了給類初始化,在執行構造方法只能類已經加載完畢,可以理解為類已經創建好了,所以是不會報錯的。
加載數據庫驅動推薦使用反射機制來創建Driver驅動的方式。
