一、什麼是反射機制
簡單的來說,反射機制指的是程式在運作時能夠取得自身的資訊。在java中,只要給定類別的名字,那麼就可以透過反射機制來獲得類別的所有資訊。
二、哪裡用到反射機制
有些時候,我們用過一些知識,但是我們不知道它的專業術語是什麼,在剛學jdbc時用過一行程式碼,Class.forName("com.mysql.jdbc.Driver.class").newInstance() ;但那時候只知道那行程式碼是產生驅動物件實例,並不知道它的具體意義。聽了反射機制這堂課後,才知道,原來這就是反射,現在很多開框架都用到反射機制,hibernate、struts都是用反射機制實現的。
三、反射機制的優點與缺點
為什麼要用反射機制?直接創建物件不就可以了嗎,這就涉及了動態與靜態的概念,
靜態編譯:在編譯時決定類型,綁定物件,即通過。
動態編譯:運行時確定類型,綁定物件。動態編譯最大限度發揮了java的靈活性,體現了多態的應用,有以降低類別之間的藕合性。
一句話,反射機制的優點就是可以實現動態創建物件和編譯,體現出很大的靈活性,特別是在J2EE的開發中它的靈活性就表現的十分明顯。例如,一個大型的軟體,不可能一次就把它設計的很完美,當這個程式編譯後,發布了,當發現需要更新某些功能時,我們不可能要用戶把以前的卸載,再重新安裝新的版本,如果這樣的話,這個軟體肯定是沒有多少人用的。採用靜態的話,需要把整個程式重新編譯一次才可以實現功能的更新,而採用反射機制的話,它就可以不用卸載,只需要在運行時才動態的創建和編譯,就可以實現該功能。
它的缺點是對性能有影響。使用反射基本上是一種解釋操作,我們可以告訴JVM,我們希望做什麼並且它滿足我們的要求。這類操作總是慢於只直接執行相同的操作。
四、利用反射機制能獲得什麼訊息
一句話,類別中有什麼訊息,它就可以獲得什麼訊息,不過前提是得知道類別的名字,要不就沒有後文了首先得根據傳入的類別的全名來創建Class物件。
Class c=Class.forName("className");註明:className必須為全名,也就是得包含套件名,例如,cn.netjava.pojo.UserInfo;
Object obj=c.newInstance();//建立物件的實例
OK,有了物件就什麼都好辦了,想要什麼資訊就有什麼資訊了。
獲得建構函數的方法
Constructor getConstructor(Class[] params)//根據指定參數取得public建構器
Constructor[] getConstructors()//取得public的所有構造器
Constructor getDeclaredConstructor(Class[] params)//根據指定參數取得public和非public的建構器
Constructor[] getDeclaredConstructors()//取得public的所有構造器
獲得類別方法的方法
Method getMethod(String name, Class[] params),根據方法名,參數型別取得方法
Method[] getMethods()//取得所有的public方法
Method getDeclaredMethod(String name, Class[] params)//根據方法名稱和參數類型,得到public和非public的方法
Method[] getDeclaredMethods()//取得所以的public和非public方法
獲得類別中屬性的方法
Field getField(String name)//根據變數名稱得到對應的public變數
Field[] getFields()//取得類別中所以public的方法
欄位 getDeclaredField(String name)//根據方法名稱取得public和非public變量
場[] getDeclaredFields()//取得類別中所有的public和非public方法
常用的就這些,知道這些,其他的都好辦…
五、用反射機制能做什麼事
剛開始在使用jdbc時侯,在編寫存取資料庫時寫到想吐,有八個表,每個表都有增刪改查中操作,那時候還不知道有反射機制這個概念,所以就對不同的表創建不同的dao類,這樣不僅開發速率地,而且代碼冗餘的厲害,最要命的是看著差不多的,然後直接複製修改,由於容易犯各種低級的錯誤(大小寫啊,多一個或少一個字母啊……),一個錯誤就可以讓你找半天。
有了java反射機制,什麼都好辦了,只需要寫一個dao類,四個方法,增刪改查,傳入不同的對象,就OK啦,無需為每個表都創建dao類,反射機制會自動幫我們完成剩下的事情,這就是它的好處。說穿了,反射機制就是專門幫我們做那些重複的有規則的事情,所以現在很多的自動生成程式碼的軟體就是運用反射機制來完成的,只要你按照規則輸入相關的參數,所以低階的程式設計師慢慢的就被抹殺了,為什麼?因為程式碼都不用寫了,隨便一個人都會開發,還要程式設計師幹嘛啊?所以我們只有一條出路,那就是努力努力再努力,成為高級程式設計師,專門開發傻瓜軟體,讓其他程式設計師到一邊涼快去,呵呵~
六、用反射機制實現資料庫資料的增、查例子
基本原理;儲存資料時,把需要儲存的物件的屬性值全部取出來再拼湊sql語句查詢時,將查詢到的資料全部包裝成一個java物件。
遊戲規則:俗話說的好,無規矩不成方圓,特別是程式來說,它只能做有規則的事情,沒有規則的它乾不了,好,那就先定規則
1)資料庫的每一個表格物件一個pojo類,表中的每一個欄位對應pojo類別的中的一個屬性。且pojo類別的名字和表格的名字相同,屬性名稱和欄位名稱相同,大小寫沒有關係,因為資料庫一般不區分大小寫
2)為pojo類別中的每一個屬性加入標準的set和get方法。
有了遊戲規則,那就開始遊戲吧。
1.首先資料庫的有一個表,假設資料庫名稱為:blogsystem,裡面的一個表名userinfo。如圖:
2、建立對應的pojo類別:
複製代碼代碼如下:
package cn.netjava.pojo;
public class UserInfo {
private int id;
private String name;
private String pwd;
private int age;
@Override
public String toString() {
return "UserInfo [id=" + id + ", name=" + name + ", pwd=" + pwd + ", age="
+ age + "]";
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
2、編寫獲得資料庫連線的工廠類別:
複製代碼代碼如下:
package cn.netjava.factory;
import java.sql.Connection;
import java.sql.DriverManager;
public class Connect2DBFactory {
public static Connection getDBConnection() {
Connection conn = null;
try {
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/blogsystem";
String user = "root";
String password = "netjava";
conn = DriverManager.getConnection(url, user, password);
} catch (Exception e) {
e.printStackTrace();
}
return conn;
}
}
3.好戲開始啦,編寫操作資料庫的dao類
複製代碼代碼如下:
package cn.netjava.session;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import cn.netjava.factory.Connect2DBFactory;
import cn.netjava.pojo.UserInfo;
public class NetJavaSession {
/**
* 解析出保存物件的sql語句
*
* @param object
* :需要保存的對象
* @return:儲存物件的sql語句
*/
public static String getSaveObjectSql(Object object) {
// 定義一個sql字串
String sql = "insert into ";
// 得到物件的類別
Class c = object.getClass();
// 得到物件中所有的方法
Method[] methods = c.getMethods();
// 得到物件中所有的屬性
Field[] fields = c.getFields();
// 得到物件類別的名字
String cName = c.getName();
// 從類別的名字解析出表名
String tableName = cName.substring(cName.lastIndexOf(".") + 1,
cName.length());
sql += tableName + "(";
List<String> mList = new ArrayList<String>();
List vList = new ArrayList();
for (Method method : methods) {
String mName = method.getName();
if (mName.startsWith("get") && !mName.startsWith("getClass")) {
String fieldName = mName.substring(3, mName.length());
mList.add(fieldName);
System.out.println("字段名字----->" + fieldName);
try {
Object value = method.invoke(object, null);
System.out.println("執行方法傳回的值:" + value);
if (value instanceof String) {
vList.add("/"" + value + "/"");
System.out.println("欄位值------>" + value);
} else {
vList.add(value);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
for (int i = 0; i < mList.size(); i++) {
if (i < mList.size() - 1) {
sql += mList.get(i) + ",";
} else {
sql += mList.get(i) + ") values(";
}
}
for (int i = 0; i < vList.size(); i++) {
if (i < vList.size() - 1) {
sql += vList.get(i) + ",";
} else {
sql += vList.get(i) + ")";
}
}
return sql;
}
public static List getDatasFromDB(String tableName, int Id) {
return null;
}
/**
* 將物件儲存到資料庫中
*
* @param object
* :需要保存的對象
* @return:方法執行的結果;1:表示成功,0:表示失敗
*/
public int saveObject(Object object) {
Connection con = Connect2DBFactory.getDBConnection();
String sql = getSaveObjectSql(object);
try {
// Statement statement=(Statement) con.createStatement();
PreparedStatement psmt = con.prepareStatement(sql);
psmt.executeUpdate();
return 1;
} catch (SQLException e) {
e.printStackTrace();
return 0;
}
}
/**
* 從資料庫取得對象
*
* @param arg0
* :物件所屬的類別
* @param id
* :物件的id
* @return:需要尋找的對象
*/
public Object getObject(String className, int Id) {
// 得到表格名字
String tableName = className.substring(className.lastIndexOf(".") + 1,
className.length());
// 根據類別名稱來建立Class對象
Class c = null;
try {
c = Class.forName(className);
} catch (ClassNotFoundException e1) {
e1.printStackTrace();
}
// 拼湊查詢sql語句
String sql = "select * from " + tableName + " where Id=" + Id;
System.out.println("找出sql語句:" + sql);
// 取得資料庫鏈接
Connection con = Connect2DBFactory.getDBConnection();
// 建立類別的實例
Object obj = null;
try {
Statement stm = con.createStatement();
// 得到執行查尋語句傳回的結果集
ResultSet set = stm.executeQuery(sql);
// 得到物件的方法數組
Method[] methods = c.getMethods();
// 遍歷結果集
while (set.next()) {
obj = c.newInstance();
// 遍歷物件的方法
for (Method method : methods) {
String methodName = method.getName();
// 如果物件的方法以set開頭
if (methodName.startsWith("set")) {
// 依照方法名字得到資料表中欄位的名字
String columnName = methodName.substring(3,
methodName.length());
// 得到方法的參數型
Class[] parmts = method.getParameterTypes();
if (parmts[0] == String.class) {
// 如果參數為String類型,則從結果集中依照列名取得對應的值,並且執行改set方法
method.invoke(obj, set.getString(columnName));
}
if (parmts[0] == int.class) {
method.invoke(obj, set.getInt(columnName));
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return obj;
}
}
4.開始測試效果怎麼樣:
複製代碼代碼如下:
package cn.netjava.tester;
import cn.netjava.pojo.UserInfo;
import cn.netjava.session.NetJavaSession;
public class Tester {
public static void main(String args[]) {
//取得NetJavaSession對象
NetJavaSession session = new NetJavaSession();
//建立一個UserInfo對象
UserInfo user = new UserInfo();
//設定物件的屬性
user.setId(6988);
user.setAge(44);
user.setPwd("pwd");
user.setName("champion");
//將物件儲存到資料庫中
String sql = session.getSaveObjectSql(user);
System.out.println("儲存物件的sql語句:" + sql);
//找對象
UserInfo userInfo = (UserInfo) session.getObject(
"cn.netjava.pojo.UserInfo", 6988);
System.out.println("取得到的資訊:" + userInfo);
}
}
5、列印出來的結果:
七、總節一下
總的來說,java反射機制是一個很好用的東西,用它可以解決很多死的東西,因為反射機制的靈活行很大,有了他,我們就不要花太多的時間來寫操做資料庫的程式碼了,而是方法更多的時間在專案的邏輯功能上,這個可以很大的減少開發時間,而且程式碼的可讀性好。先在的許多開源框架都是才用的反射機制,它只要配置文件,然後按規則來呼叫他的方法就可以了。