try { Class.forName("com.mysql.jdbc.Driver") ; } catch(ClassNotFoundException e) { System.out.println("找不到驅動程序類 ,加載驅動失敗!"); // TODO }
在上面的代碼中,class.forName("com.mysql.jdbc.Driver");的主要作用是在運行期以反射的方式來檢查JDBC驅動的主類com.mysql.jdbc.Driver是否存在,若不存則表示運行環境中沒有這個驅動,進入catch段。如果你確定一定以及肯定它會存在,可以直接寫成import com.mysql.jdbc.Driver;效果基本是一樣的(只是在編譯期及運行期要都保證此類存在classpath中)所以,以反射形式加載的一個好處是當驅動jar包不存在時,我們可以做更多的操作。(要知道,在很久很久以前,jdbc驅動一般都是放在運行環境的classpath中的,如tomcat/lib
)。
另外一個很重要的原因是解耦。首先要明白JDBC是Java的一種規范,通俗一點說就是JDK在java.sql.*
下提供了一系列的接口(interface),但沒有提供任何實現(也就是類)。 所以任何人都可以在接口規范之下寫自己的JDBC實現(如MySQL)。而若調用者也只調用接口上的方法(如我們),那麼當未來有任何變更需要時(例如要從MySQL遷移至Oracle),則理論上不需要對代碼做任何修改就能直接切換(可惜SQL語法沒能統一規范)這意味著什麼?意味著你的代碼中不應該引用任何與實現相關的東西,你的代碼只知道java.sql.*
,而不應該知道com.mysql.*
或是com.oracle.*
,以避免或減少未來切換數據源時對代碼的變更。注意,我們使用的所有其他API包括Connection
/Statement
/ResultSet
等都是java.sql.*
的東西,甚至com.mysql.jdbc.Driver
類也是:因此,直接import com.mysql.jdbc.Driver;
違反了開閉原則(OCP,對擴展開放,對修改關閉)。(有人說我用反射也必須要修改代碼呀,事實上你可以將類名字符串存儲至.properties文件,和數據庫用戶名密碼放在一起,就像Hibernate做的那樣)。
如果我可以保證JDBC驅動一定在classpath下,是不是可以不寫這段反射代碼,也不引用任何的Driver類?答案是否定的,請看下面這段代碼源自com.mysql.jdbc.Driver
:
package com.mysql.jdbc; public class Driver extends NonRegisteringDriver implements java.sql.Driver { // // Register ourselves with the DriverManager // static { try { java.sql.DriverManager.registerDriver(new Driver()); } catch (SQLException E) { throw new RuntimeException("Can't register driver!"); } } ... }
static代碼塊會在類加載時就被執行,也就是當我們執行Class.forName("com.mysql.jdbc.Driver")
時(或import com.mysql.jdbc.Driver
)
反射的使用,讓Java更具有動態特性。一條sql語句查詢,得到一條查詢結果,例如select * from user where id = 1;得到id, userName, email, pwd等字段以及對應的值,但是你的目的不僅僅是得到這些字段,你要將這些字段封裝成User對象,便於後續的使用。
如果你只有一個User類你可以寫一個util類將這些字段一一賦值給User對象的每個屬性,但是你在項目開發中還有很多實體類,這怎麼辦呢?每個都寫個util類?對象屬性發生變化又得重寫?這時候反射機制就可以實現這個通用的方法,傳入查詢出的ResultSet以及你要的對象Class,通過反射獲取Class中的field list,從ResultSet中獲取對應的值,再使用反射調用Class中對應filed的set方法,完成對對象的封裝。