java學習--反射

一、什麼是反射機制 
       
簡單的來說,反射機制指的是程序在運行時能夠獲取自身的信息。在java中,只要給定類的名字, 那麼就可以通過反射機制來獲得類的所有信息。 JAVA反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意一個方法;這種動態獲取的信息以及動態調用對象的方法的功能稱爲java語言的反射機制。

二、利用反射機制能獲得什麼信息 
    一句話,類中有什麼信息,它就可以獲得什麼信息,不過前提是得知道類的名字,要不就沒有後文了 
 首先得根據傳入的類的全名來創建Class對象。 

如何獲取類(三種方式):

//第一種方式:  
Classc1 = Class.forName("Employee");  
//第二種方式:  
//java中每個類型都有class 屬性.  
Classc2 = Employee.class;  
   
//第三種方式:  
//java語言中任何一個java對象都有getClass 方法  
Employeee = new Employee();  
Classc3 = e.getClass(); //c3是運行時類 (e的運行時類是Employee)  


    
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的方法

    Field getDeclaredField(String name)//根據方法名獲得public和非public變量

    Field[] getDeclaredFields()//獲得類中所有的public和非public方法 

    常用的就這些,知道這些,其他的都好辦…… 

Java反射機制主要提供了以下功能:

  1.在運行時判斷任意一個對象所屬的類。

public Class getClass(Object obj) {
return obj.getClass();
}
public boolean isInstance(Object obj, Class cls) {  
     return cls.isInstance(obj);  
}

  2.在運行時構造任意一個類的對象。

Class c =Class.forName("Employee");  
  
//創建此Class 對象所表示的類的一個新實例  
Objecto = c.newInstance(); //調用了Employee的無參數構造方法.

  3.在運行時判斷任意一個類所具有的成員變量和方法。

所有屬性:

            Class c = Class.forName("java.lang.Integer");  
              //獲取所有的屬性?  
            Field[] fs = c.getDeclaredFields();  
       
                   //定義可變長的字符串,用來存儲屬性  
            StringBuffer sb = new StringBuffer();  
            //通過追加的方法,將每個屬性拼接到此字符串中  
            //最外邊的public定義  
            sb.append(Modifier.toString(c.getModifiers()) + " class " + c.getSimpleName() +"{\n");  
            //裏邊的每一個屬性  
            for(Field field:fs){  
                sb.append("\t");//空格  
                sb.append(Modifier.toString(field.getModifiers())+" ");//獲得屬性的修飾符,例如public,static等等  
                sb.append(field.getType().getSimpleName() + " ");//屬性的類型的名字  
                sb.append(field.getName()+";\n");//屬性的名字+回車  
            }  
      
            sb.append("}");  
      
            System.out.println(sb);  

指定屬性:

Class c = Class.forName("User");  
    //獲取id屬性  
    Field idF = c.getDeclaredField("id");  
    //實例化這個類賦給o  
    Object o = c.newInstance();  
    //打破封裝  
    idF.setAccessible(true); //使用反射機制可以打破封裝性,導致了java對象的屬性不安全。  
    //給o對象的id屬性賦值"110"  
    idF.set(o, "110"); //set  
    //get  
    System.out.println(idF.get(o));  



  4.在運行時調用任意一個對象的方法。

public Object invokeMethod(Object owner, String methodName, Object[] args) throws Exception {  
   
     Class ownerClass = owner.getClass();  
   
     Class[] argsClass = new Class[args.length];  
   
     for (int i = 0, j = args.length; i < j; i++) {  
         argsClass[i] = args[i].getClass();  
     }  
  
      Method method = ownerClass.getMethod(methodName,argsClass);  
   //method.invoke(null, args);  靜態方法
     return method.invoke(owner, args); 
} 



三、反射機制的優點與缺點 
       爲什麼要用反射機制?直接創建對象不就可以了嗎,這就涉及到了動態與靜態的概念, 
    靜態編譯:在編譯時確定類型,綁定對象,即通過。 
    動態編譯:運行時確定類型,綁定對象。動態編譯最大限度發揮了java的靈活性,體現了多 態的應用,有以降低類之間的藕合性。 
    一句話,反射機制的優點就是可以實現動態創建對象和編譯,體現出很大的靈活性,特別是在J2EE的開發中它的靈活性就表現的十分明顯。比如,一個大型的軟件,不可能一次就把把它設計的很完美,當這個程序編 譯後,發佈了,當發現需要更新某些功能時,我們不可能要用戶把以前的卸載,再重新安裝新的版本,假如 這樣的話,這個軟件肯定是沒有多少人用的。採用靜態的話,需要把整個程序重新編譯一次纔可以實現功能 的更新,而採用反射機制的話,它就可以不用卸載,只需要在運行時才動態的創建和編譯,就可以實現該功能。 
       它的缺點是對性能有影響。使用反射基本上是一種解釋操作,我們可以告訴JVM,我們希望做什麼並且它滿足我們的要求。這類操作總是慢於只直接執行相同的操作。 

四、用反射機制能幹什麼事 

反射的適用場景是什麼?

1).Java的反射機制在做基礎框架的時候非常有用,有一句話這麼說來着:反射機制是很多Java框架的基石。而一般應用層面很少用,不過這種東西,現在很多開源框架基本都已經給你封裝好了,自己基本用不着寫。典型的除了Hibernate之外,還有Spring也用到很多反射機制。經典的就是在xml文件或者properties裏面寫好了配置,然後在Java類裏面解析xml或properties裏面的內容,得到一個字符串,然後用反射機制,根據這個字符串獲得某個類的Class實例,這樣就可以動態配置一些東西,不用每一次都要在代碼裏面去new或者做其他的事情,以後要改的話直接改配置文件,代碼維護起來就很方便了,同時有時候要適應某些需求,Java類裏面不一定能直接調用另外的方法,這時候也可以通過反射機制來實現
總的來說,自己寫的很少,具體什麼時候要用那要看需求,反射機制無非就是根據一個String來得到你要的實體對象,然後調用它原來的東西。但是如果是要自己寫框架的話,那就會用得比較多了

2)當你做一個軟件可以安裝插件的功能,你連插件的類型名稱都不知道,你怎麼實例化這個對象呢?因爲程序是支持插件的(第三方的),在開發的時候並不知道 。所以無法在代碼中 New出來 ,但反射可以,通過反射,動態加載程序集,然後讀出類,檢查標記之後再實例化對象,就可以獲得正確的類實例。

3)在編碼階段不知道那個類名,要在運行期從配置文件讀取類名, 這時候就沒有辦法硬編碼new ClassName(),而必須用到反射才能創建這個對象.反射的目的就是爲了擴展未知的應用。比如你寫了一個程序,這個程序定義了一些接口,只要實現了這些接口的dll都可以作爲插件來插入到這個程序中。那麼怎麼實現呢?就可以通過反射來實現。就是把dll加載進內存,然後通過反射的方式來調用dll中的方法。很多工廠模式就是使用的反射。 


例子:Spring框架中的反射:

Spring中使用Class實例化

bean.xml
<bean id="id" class="com.xy.Student" />


Spring將採用的代碼創建代碼Java實例(bean)

Class c = Class.forName("com.xy.Student");
Object bean = c.newInstance();


Spring調用getter,setter方法,,經典的依賴注入

我們以setter注入例子

bean.xml
<bean id="id" class="com.xy.Student">
    <property name="stuName" value="xy" />
</bean>


Spring將採用的代碼創建代碼Java實例,並注入值:

Class c = Class.forName("com.xy.Student");
Object bean = c.newInstance();

通過一些操作獲取對stuName對應的setter方法名

String setname = "set" + "StuName";
Method method = c.getMehod(setname,String.Class);
method.invoke(bean,"xy");


這樣就完成了最基本的注入操作。當然,Spring還可以通過構造函數進行注入。這樣就參考第二點有參構造的Class的使用。

Class還可以訪問Annotation,這樣就Spring使用註解的時候,可以完成注入的功能。


五、用反射機制實現對數據庫數據的增、查例子 

剛開始在使用jdbc時侯,在編寫訪問數據庫時寫到想吐,有八個表,每個表都有增刪改查中操作 那時候還不知道有反射機制這個概念,所以就對不同的表創建不同的dao類,這樣不僅開發速率地,而且代碼 冗餘的厲害,最要命的是看着差不多的,然後直接複製修改,由於容易犯各種低級的錯誤(大小寫啊,多一 個或少一個字母啊……),一個錯誤就可以讓你找半天。 
        有了java反射機制,什麼都好辦了,只需要寫一個dao類,四個方法,增刪改查,傳入不同的對象,就OK啦, 無需爲每一個表都創建dao類,反射機制會自動幫我們完成剩下的事情,這就是它的好處。說白了,反射機制就是專門 幫我們做那些重複的有規則的事情,所以現在很多的自動生成代碼的軟件就是運用反射機制來完成的,只要你按照規則 輸入相關的參數,所以低級的程序員慢慢的就被抹殺了,爲什麼?因爲代碼都不用寫了,隨便一個人都會開發,還要程序員幹什麼啊?所以我們只有一條出路,那就是努力努力再努力,成爲高級程序員,專門開發傻瓜軟件,讓其他程序員  到 一邊涼快去,呵呵~ 
    基本原理;保存數據時,把需要保存的對象的屬性值全部取出來再拼湊sql語句 
                 查詢時,將查詢到的數據全部包裝成一個java對象。 
    遊戲規則:俗話說的好,無規矩不成方圓,特別是程序來說,它只能做有規則的事情,沒有規則的它幹不了,好,那就 
              先定規則 
              1)數據庫的每一個表對象一個pojo類,表中的每一個字段對應pojo類的中的一個屬性。 
                 並且pojo類的名字和表的名字相同,屬性名和字段名相同,大小寫沒有關係,因爲數據庫一般不區分大小寫  
              2)爲pojo類中的每一個屬性添加標準的set和get方法。 
    有了遊戲規則,那麼開始遊戲吧。

1、首先數據庫的有一個表,假設數據庫名稱爲:blogsystem,裏面的一個表名userinfo。如圖:

2、創建對應的pojo類:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package cn.netjava.pojo;
 
publicclass UserInfo {
privateint id;
private String name;
private String pwd;
privateint age;
 
@Override
public String toString() {
    return "UserInfo [id=" + id + ", name=" + name+ ", pwd=" + pwd+ ", age="
            + age+ "]";
}
publicint 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;
}
publicint getAge() {
    return age;
}
public void setAge(int age) {
    this.age= age;
}
 
}
2、編寫獲得數據庫連接的工廠類:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package cn.netjava.factory;
 
import java.sql.Connection;
import java.sql.DriverManager;
 
publicclass 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類

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
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;
 
publicclass NetJavaSession {
    /**
     * 解析出保存對象的sql語句
     *
     * @paramobject
     *            :需要保存的對象
     * @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;
 
    }
 
    /**
     * 將對象保存到數據庫中
     *
     * @paramobject
     *            :需要保存的對象
     * @return:方法執行的結果;1:表示成功,0:表示失敗
     */
    publicint 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
     *            :對象所屬的類
     * @paramid
     *            :對象的id
     * @return:需要查找的對象
     */
    publicObject 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();
            // 得到執行查尋語句返回的結果集
            ResultSetset = 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、開始測試效果怎麼樣:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package cn.netjava.tester;
 
import cn.netjava.pojo.UserInfo;
import cn.netjava.session.NetJavaSession;
 
publicclass 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反射機制是一個很好用的東西,用它可以解決很多死的東西,因爲反射機制的靈活行很大,有了他,我們就不要花太多的時間來寫操做數據庫的代碼了,而是方法更多的時間在項目的邏輯功能上,這個可以很大的減少開發時間,而且代碼的可讀性好。先在的很多開源框架都是才用的反射機制,它只要配置文件,然後按規則來調用他的方法就可以了。


發佈了27 篇原創文章 · 獲贊 67 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章