什么是反射
反射就是指程序在運行的時候可以知道一個類的自身信息。
對于任何一個類:可以知道這個類的屬性和方法。
對于任何一個對象:可以調(diào)用這個對象的任何一個方法和屬性。
反射就是把java類中的各種成分映射成一個個的Java對象
例如:一個類有:成員變量、方法、構(gòu)造方法、包等等信息,利用反射技術(shù)可以對一個類進行?解剖,把個個?組成部分映射成一個個對象。
(其實:一個類中這些成員方法、構(gòu)造方法、在加入類中都有一個類來描述)
反射的功能
在運行時判斷任意一個對象所屬的類
在運行的時候構(gòu)造任意一個類的對象
在運行時判斷一個類所具有的成員變量和方法
在運行時調(diào)用任何一個對象的方法
生成動態(tài)代理(會有一篇關(guān)于動態(tài)代理的文章,在這里挖坑)
反射的優(yōu)點和缺點
動態(tài)編譯和靜態(tài)編譯
反射用到的是動態(tài)編譯,既然有動態(tài)編譯,就會有靜態(tài)編譯
那么動態(tài)編譯和靜態(tài)編譯又有什么區(qū)別?
靜態(tài)編譯:在編譯的時候進確定類型,如果綁定對象成功,new?是靜態(tài)加載類,就編譯通過。
1 代碼示例

2 解釋
當在Phone.java里面寫好代碼的時候,如果需要添加新的類,則需要直接在文件里面修改代碼。假如需要添加一個華為手機,則我需要在Phone.java文件里面加個if語句判斷傳進來的參數(shù)是不是"huawei",這樣增加了類之間的耦合性。
當刪除一個類的時候Phone.java編譯可能會出現(xiàn)錯誤。??假如我刪除了小米手機這個類,phone.java文件沒有刪除if判斷語句,那么phone.java在編譯的時候則會失敗。
沒刪除Xiaomi.java編譯的時候是成功并且成功運行


刪除Xiaomi.java編譯的時候就會失敗了,因為Xiaomi.java不存在


動態(tài)編譯:在運行的時候確定類型,綁定對象。最大發(fā)揮了Java的多態(tài),降低類之間的耦合性。
1 代碼示例
Phone.java

2 解釋
(1)對比靜態(tài)編譯,當我們需要往Phone.java里面?zhèn)鬟f新的類參數(shù)的時候,根本不需要修改Phone.java的代碼,因為這里應用了Java的多態(tài)。只要新建一個新的類實現(xiàn)了PhoneInterface的接口,把類名傳進去就可以調(diào)用。這里體現(xiàn)了?需要哪個類的對象就動態(tài)的創(chuàng)建哪個類的對象,也就是說動態(tài)的實現(xiàn)了類的加載。

(2)當刪除一個類的時候,Phone.java文件不會編譯失敗。
比如說刪除OnePlus.java


區(qū)別:這里說明了動態(tài)加載的在不修改Phone.java的前提下不會因為其它類的不存在而導致整個文件不能編譯,而靜態(tài)加載則會編譯的時候綁定對象,從而導致編譯失敗。
優(yōu)點
以實現(xiàn)動態(tài)創(chuàng)建對象和編譯,體現(xiàn)出很大的靈活性,特別是在J2EE的開發(fā)中它的靈活性就表現(xiàn)的十分明顯。比如,一個大型的軟件,不可能一次就把把它設(shè)計的很完美,當這個程序編譯后,發(fā)布了,當發(fā)現(xiàn)需要更新某些功能時,我們不可能要用戶把以前的卸載,再重新安裝新的版本,假如這樣的話,這個軟件肯定是沒有多少人用的。采用靜態(tài)的話,需要把整個程序重新編譯一次才可以實現(xiàn)功能的更新,而采用反射機制的話,它就可以不用卸載,只需要在運行時才動態(tài)的創(chuàng)建和編譯,就可以實現(xiàn)該功能。
缺點
對性能有影響。使用反射基本上是一種解釋操作,我們可以告訴JVM,我們希望做什么并且它滿足我們的要求。這類操作總是慢于只直接執(zhí)行相同的操作。
Class類和類類型
Class類
所有的類是java.lang.Class類的對象,Class類是所有類的類,反射的基礎(chǔ)。
Class對象(類類型)
普通類構(gòu)造對象是:Student?s?=?new?Student();
但Class對象則不是,看Class類的源碼,構(gòu)造器是私有的,則用戶無法直接像普通類那樣new一個Class的對象,只有JVM才可以構(gòu)造Class對象。
private Class(ClassLoader loader) { // Initialize final field for classLoader. The initialization value of non-null // prevents future JIT optimizations from assuming this final field is null. classLoader = loader; }
但是我們可以通過一個已知的類獲得Class對象
有以下三種方式:
Class s = Student.class;
Class s1 = new Student().getClass();
Class s2 = Class.forName("Student");
Class對象就是類類型,在這里表示的是Student類的類型,下面看一個圖了解Class對象是什么和類在JVM中加載的過程

由圖中可以看出,一個具體的Class對象就保存了具體類的基本屬性和方法,并且可以調(diào)用。
Student類

反射相關(guān)操作
文章開始說過,反射會把一個類的成分(成員變量,方法,構(gòu)造器)各自映射成一個個對象(Field對象,Method對象,Construct對象),
一個類中這些成員方法、構(gòu)造方法、在加入類中都有一個類來描述。
java.lang.reflect.Constructor; java.lang.reflect.Field;? java.lang.reflect.Method;? java.lang.reflect.Modifier;
通過Class對象我們可以做什么呢?
獲取成員方法Method
獲取成員變量Field
獲取構(gòu)造函數(shù)Construct
獲取成員方法
用法

例子

結(jié)果

只要知道包的限定名,就可以對Student這個類進行所有操作
獲取成員變量信息
成員變量 = 成員類型+變量名
用法
獲取成員變量,通過Class類的以下方法,變量是成員變量名

例子

獲取構(gòu)造函數(shù)信息
獲取構(gòu)造函數(shù),Class的方法如下
用法

例子
Student類的私有構(gòu)造函數(shù)

獲取私有的構(gòu)造函數(shù),并且設(shè)置為public從而可以創(chuàng)建對象

結(jié)果

實現(xiàn)對數(shù)據(jù)庫增,查。
原理
保存數(shù)據(jù)時:把pojo類的屬性取出來,拼湊sql語句
查詢數(shù)據(jù)的時:把查詢到的數(shù)據(jù)包裝成一個Java對象
一張數(shù)據(jù)表對應java的一個pojo對象,表中的每一個字段(column)對應pojo的每一個屬性
數(shù)據(jù)表名和Pojo的類名相等,column和pojo的屬性相等,不區(qū)分大小寫(數(shù)據(jù)庫中不區(qū)分大小寫)
pojo的每一個屬性的get和set方法,都是為了后續(xù)的操作
實例
數(shù)據(jù)表User

pojo User類
package dbtest;
public class User {
? ? private int id;
? ? private String name;
? ? private String pwd;
? ? private int age;
? ? @Override
publicStringtoString()
{
? ? ? ? return "User{" +
? ? ? ? ? ? ? ? "id=" + id +
? ? ? ? ? ? ? ? ", name='" + name + '\'' +
? ? ? ? ? ? ? ? ", pwd='" + pwd + '\'' +
? ? ? ? ? ? ? ? ", age=" + age +
? ? ? ? ? ? ? ? '}';
? ? }
? ? publicintgetId(){
? ? ? ? return id;
? ? }
? ? publicvoidsetId(intid){
? ? ? ? this.id = id;
? ? }
? ? publicStringgetName(){
? ? ? ? return name;
? ? }
? ? publicvoidsetName(String name){
? ? ? ? this.name = name;
? ? }
? ? publicStringgetPwd(){
? ? ? ? return pwd;
? ? }
? ? publicvoidsetPwd(String pwd){
? ? ? ? this.pwd = pwd;
? ? }
? ? publicintgetAge(){
? ? ? ? return age;
? ? }
? ? publicvoidsetAge(intage){
? ? ? ? this.age = age;
? ? }
}
數(shù)據(jù)庫連接的工廠類

操作數(shù)據(jù)庫的dao
package dbtest;
import org.springframework.web.bind.annotation.ResponseBody;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
public class SqlSession {
? ? publicstaticStringgetSaveObjectSql(Object o)throws InvocationTargetException, IllegalAccessException{
? ? ? ? String sql = "insert into ";
? ? ? ? /*獲取Class對象*/
? ? ? ? Class c = o.getClass();
? ? ? ? /*獲取pojo所有的方法*/
? ? ? ? Method[] methods = c.getDeclaredMethods();
? ? ? ? /*獲取全類名*/
? ? ? ? String cName = c.getName();
? ? ? ? /*通過全類名獲取數(shù)據(jù)庫名稱*/
? ? ? ? String tableName = cName.substring(cName.lastIndexOf(".")+1,cName.length());
? ? ? ? sql+= tableName + "(";
? ? ? ? /*字段名字*/
? ? ? ? List<String> fieldList = new ArrayList<>();
? ? ? ? /*字段對應的值*/
? ? ? ? List valueList = new ArrayList();
? ? ? ? /*遍歷Class對象的Method對象,就可以執(zhí)行相對于的方法了*/
? ? ? ? for?(Method?method?:
? ? ? ? ? ? ? ? methods) {
? ? ? ? ? ? String methodName = method.getName();
? ? ? ? ? ? /*找出get方法,并設(shè)置值*/
? ? ? ? ? ? if(methodName.startsWith("get")?&&?!method.equals("getClass")){
? ? ? ? ? ? ? ? String fieldName = methodName.substring(3,methodName.length());
? ? ? ? ? ? ? ? fieldList.add(fieldName);
? ? ? ? ? ? ? ? Object res = method.invoke(o,null);
? ? ? ? ? ? ? ? if(res instanceof String){
? ? ? ? ? ? ? ? ? ? valueList.add("\""+res+"\"");
? ? ? ? ? ? ? ? }else{
? ? ? ? ? ? ? ? ? ? valueList.add(res);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? /*拼接sql語句的字段*/
? ? ? ? for (int i = 0; i <fieldList.size() ; i++) {
? ? ? ? ? ? if(i < fieldList.size() - 1){
? ? ? ? ? ? ? ? sql += fieldList.get(i) + ",";
? ? ? ? ? ? }else{
? ? ? ? ? ? ? ? sql += fieldList.get(i) + ") values (";
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? /*拼接sql語句的值*/
? ? ? ? for (int i = 0; i <valueList.size() ; i++) {
? ? ? ? ? ? if(i < valueList.size()-1){
? ? ? ? ? ? ? ? sql += valueList.get(i) + ",";
? ? ? ? ? ? }else{
? ? ? ? ? ? ? ? sql += valueList.get(i) + ")";
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? return sql;
? ? }
? ? /*保存數(shù)據(jù)的操作*/
? ? publicintsaveObject(Object o)throws InvocationTargetException, IllegalAccessException, SQLException{
? ? ? ? Connection connection = ConnectDBFactory.getDBConnection();
? ? ? ? String sql = getSaveObjectSql(o);
? ? ? ? PreparedStatement statement = connection.prepareStatement(sql);
? ? ? ? int i = 0;
? ? ? ? i = statement.executeUpdate();
? ? ? ? return i;
? ? }
? ? /*
* 查詢數(shù)據(jù),查詢出來的數(shù)據(jù)映射到pojo的每一個屬性上
* */
? ? publicObjectgetObject(String pname,intid)throws ClassNotFoundException{
? ? ? ? /*通過包名獲取數(shù)據(jù)表名*/
? ? ? ? String tableName =? pname.substring(pname.lastIndexOf(".")+1,pname.length());
? ? ? ? String sql = "select * from " + tableName + " where Id = " + id;
? ? ? ? Connection conn = ConnectDBFactory.getDBConnection();
? ? ? ? Class c = Class.forName(pname);
? ? ? ? Object obj = null;
? ? ? ? try{
? ? ? ? ? ? Statement statement = conn.createStatement();
? ? ? ? ? ? ResultSet resultSet = statement.executeQuery(sql);
? ? ? ? ? ? Method[] methods = c.getDeclaredMethods();
? ? ? ? ? ? while(resultSet.next()){
? ? ? ? ? ? ? ? obj = c.newInstance();
? ? ? ? ? ? ? ? for?(Method?method?:methods
? ? ? ? ? ? ? ? ? ? ? ? ) {
? ? ? ? ? ? ? ? ? ? String methodName = method.getName();
? ? ? ? ? ? ? ? ? ? if(methodName.startsWith("set")){
? ? ? ? ? ? ? ? ? ? ? ? /*通過方法名獲取數(shù)據(jù)庫的列名*/
? ? ? ? ? ? ? ? ? ? ? ? String columnName = methodName.substring(3,methodName.length());
? ? ? ? ? ? ? ? ? ? ? ? /*獲取參數(shù)的類型*/
? ? ? ? ? ? ? ? ? ? ? ? Class[] params = method.getParameterTypes();
? ? ? ? ? ? ? ? ? ? ? ? /*判斷參數(shù)的類型*/
? ? ? ? ? ? ? ? ? ? ? ? if(params[0] == String.class){
? ? ? ? ? ? ? ? ? ? ? ? ? ? method.invoke(obj,resultSet.getString(columnName));
? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? ? ? if(params[0] == int.class){
? ? ? ? ? ? ? ? ? ? ? ? ? ? method.invoke(obj,resultSet.getInt(columnName));
? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }catch (Exception e){
? ? ? ? ? ? e.printStackTrace();
? ? ? ? }
? ? ? ? return obj;
? ? }
? ? publicstaticvoidmain(String[] args){
? ? ? ? try{
? ? ? ? ? ? SqlSession session = new SqlSession();
? ? ? ? ? ? User user = new User();
? ? ? ? ? ? user.setAge(22);
? ? ? ? ? ? user.setName("JiemingLi");
? ? ? ? ? ? user.setId(44);
? ? ? ? ? ? user.setPwd("123456");
? ? ? ? ? ? int resNum? = session.saveObject(user);
? ? ? ? ? ? if(resNum > 0){
? ? ? ? ? ? ? ? System.out.println("成功");
? ? ? ? ? ? }else{
? ? ? ? ? ? ? ? System.out.println("插入失敗");
? ? ? ? ? ? }
? ? ? ? ? ? User res = (User)session.getObject("dbtest.User",44);
? ? ? ? ? ? System.out.println(res);
? ? ? ? }catch (Exception e){
? ? ? ? ? ? e.printStackTrace();
? ? ? ? }
? ? }
}
結(jié)果

總結(jié)
Java反射非常好用,靈活性非常大,不用花費太多的時間去寫操作數(shù)據(jù)庫的代碼,讓重點在開發(fā)者的業(yè)務邏輯上?,F(xiàn)在很多和數(shù)據(jù)庫操作的框架都用到反射,只要配置文件,按照框架的規(guī)則就可以對數(shù)據(jù)庫進行相對應的操作了。