Java ?Reflection?(JAVA反射)????

          Reflection?是?Java?程序開發語言的特征之一,它允許運行中的?Java?程序對自身進行檢查,或者說“自審”,并能直接操作程序的內部屬性。例如,使用它能獲得?Java?類中各成員的名稱并顯示出來。

          Java?的這一能力在實際應用中也許用得不是很多,但是在其它的程序設計語言中根本就不存在這一特性。例如,Pascal、C?或者?C++?中就沒有辦法在程序中獲得函數定義相關的信息。

          JavaBean?是?reflection?的實際應用之一,它能讓一些工具可視化的操作軟件組件。這些工具通過?reflection?動態的載入并取得?Java?組件(類)?的屬性。



          1.?一個簡單的例子

          考慮下面這個簡單的例子,讓我們看看?reflection?是如何工作的。

          import?java.lang.reflect.*;
          public?class?DumpMethods?{
          ???public?static?void?main(String?args[])?{
          ???????try?{
          ???????????Class?c?=?Class.forName(args[0]);
          ???????????Method?m[]?=?c.getDeclaredMethods();
          ???????????for?(int?i?=?0;?i?<?m.length;?i++)
          ???????????????System.out.println(m[i].toString());
          ???????}?catch?(Throwable?e)?{
          ???????????System.err.println(e);
          ???????}
          ???}
          }

          按如下語句執行:

          java?DumpMethods?java.util.Stack

          它的結果輸出為:

          public?java.lang.Object?java.util.Stack.push(java.lang.Object)

          public?synchronized?java.lang.Object?java.util.Stack.pop()

          public?synchronized?java.lang.Object?java.util.Stack.peek()

          public?boolean?java.util.Stack.empty()

          public?synchronized?int?java.util.Stack.search(java.lang.Object)

          這樣就列出了java.util.Stack?類的各方法名以及它們的限制符和返回類型。

          這個程序使用?Class.forName?載入指定的類,然后調用?getDeclaredMethods?來獲取這個類中定義了的方法列表。java.lang.reflect.Methods?是用來描述某個類中單個方法的一個類。

          2.開始使用?Reflection

          用于?reflection?的類,如?Method,可以在?java.lang.relfect?包中找到。使用這些類的時候必須要遵循三個步驟:第一步是獲得你想操作的類的?java.lang.Class?對象。在運行中的?Java?程序中,用?java.lang.Class?類來描述類和接口等。

          下面就是獲得一個?Class?對象的方法之一:

          Class?c?=?Class.forName("java.lang.String");

          這條語句得到一個?String?類的類對象。還有另一種方法,如下面的語句:

          Class?c?=?int.class;

          或者

          Class?c?=?Integer.TYPE;

          它們可獲得基本類型的類信息。其中后一種方法中訪問的是基本類型的封裝類?(如?Integer)?中預先定義好的?TYPE?字段。

          第二步是調用諸如?getDeclaredMethods?的方法,以取得該類中定義的所有方法的列表。

          一旦取得這個信息,就可以進行第三步了??使用?reflection?API?來操作這些信息,如下面這段代碼:

          Class?c?=?Class.forName("java.lang.String");

          Method?m[]?=?c.getDeclaredMethods();

          System.out.println(m[0].toString());

          它將以文本方式打印出?String?中定義的第一個方法的原型。

          在下面的例子中,這三個步驟將為使用?reflection?處理特殊應用程序提供例證。

          模擬?instanceof?操作符

          得到類信息之后,通常下一個步驟就是解決關于?Class?對象的一些基本的問題。例如,Class.isInstance?方法可以用于模擬?instanceof?操作符:

          class?A?{
          }

          public?class?instance1?{
          ???public?static?void?main(String?args[])?{
          ???????try?{
          ???????????Class?cls?=?Class.forName("A");
          ???????????boolean?b1?=?cls.isInstance(new?Integer(37));
          ???????????System.out.println(b1);
          ???????????boolean?b2?=?cls.isInstance(new?A());
          ???????????System.out.println(b2);
          ???????}?catch?(Throwable?e)?{
          ???????????System.err.println(e);
          ???????}
          ???}
          }

          在這個例子中創建了一個?A?類的?Class?對象,然后檢查一些對象是否是?A?的實例。Integer(37)?不是,但?new?A()?是。

          3.找出類的方法

          找出一個類中定義了些什么方法,這是一個非常有價值也非常基礎的?reflection?用法。下面的代碼就實現了這一用法:

          import?java.lang.reflect.*;

          public?class?method1?{
          ???private?int?f1(Object?p,?int?x)?throws?NullPointerException?{
          ???????if?(p?==?null)
          ???????????throw?new?NullPointerException();
          ???????return?x;
          ???}

          ???public?static?void?main(String?args[])?{
          ???????try?{
          ???????????Class?cls?=?Class.forName("method1");
          ???????????Method?methlist[]?=?cls.getDeclaredMethods();
          ???????????for?(int?i?=?0;?i?<?methlist.length;?i++)?{
          ???????????????Method?m?=?methlist[i];
          ???????????????System.out.println("name?=?"?+?m.getName());
          ???????????????System.out.println("decl?class?=?"?+?m.getDeclaringClass());
          ???????????????Class?pvec[]?=?m.getParameterTypes();
          ???????????????for?(int?j?=?0;?j?<?pvec.length;?j++)
          ???????????????????System.out.println("param?#"?+?j?+?"?"?+?pvec[j]);
          ???????????????Class?evec[]?=?m.getExceptionTypes();
          ???????????????for?(int?j?=?0;?j?<?evec.length;?j++)
          ???????????????????System.out.println("exc?#"?+?j?+?"?"?+?evec[j]);
          ???????????????System.out.println("return?type?=?"?+?m.getReturnType());
          ???????????????System.out.println("-----");
          ???????????}
          ???????}?catch?(Throwable?e)?{
          ???????????System.err.println(e);
          ???????}
          ???}
          }

          這個程序首先取得?method1?類的描述,然后調用?getDeclaredMethods?來獲取一系列的?Method?對象,它們分別描述了定義在類中的每一個方法,包括?public?方法、protected?方法、package?方法和?private?方法等。如果你在程序中使用?getMethods?來代替?getDeclaredMethods,你還能獲得繼承來的各個方法的信息。

          取得了?Method?對象列表之后,要顯示這些方法的參數類型、異常類型和返回值類型等就不難了。這些類型是基本類型還是類類型,都可以由描述類的對象按順序給出。

          輸出的結果如下:

          name?=?f1

          decl?class?=?class?method1

          param?#0?class?java.lang.Object

          param?#1?int

          exc?#0?class?java.lang.NullPointerException

          return?type?=?int

          -----

          name?=?main

          decl?class?=?class?method1

          param?#0?class?[Ljava.lang.String;

          return?type?=?void

          -----


          4.獲取構造器信息

          獲取類構造器的用法與上述獲取方法的用法類似,如:

          import?java.lang.reflect.*;

          public?class?constructor1?{
          ???public?constructor1()?{
          ???}

          ???protected?constructor1(int?i,?double?d)?{
          ???}

          ???public?static?void?main(String?args[])?{
          ???????try?{
          ???????????Class?cls?=?Class.forName("constructor1");
          ???????????Constructor?ctorlist[]?=?cls.getDeclaredConstructors();
          ???????????for?(int?i?=?0;?i?<?ctorlist.length;?i++)?{
          ???????????????Constructor?ct?=?ctorlist[i];
          ???????????????System.out.println("name?=?"?+?ct.getName());
          ???????????????System.out.println("decl?class?=?"?+?ct.getDeclaringClass());
          ???????????????Class?pvec[]?=?ct.getParameterTypes();
          ???????????????for?(int?j?=?0;?j?<?pvec.length;?j++)
          ???????????????????System.out.println("param?#"?+?j?+?"?"?+?pvec[j]);
          ???????????????Class?evec[]?=?ct.getExceptionTypes();
          ???????????????for?(int?j?=?0;?j?<?evec.length;?j++)
          ???????????????????System.out.println("exc?#"?+?j?+?"?"?+?evec[j]);
          ???????????????System.out.println("-----");
          ???????????}
          ???????}?catch?(Throwable?e)?{
          ???????????System.err.println(e);
          ???????}
          ???}
          }

          這個例子中沒能獲得返回類型的相關信息,那是因為構造器沒有返回類型。

          這個程序運行的結果是:

          name?=?constructor1

          decl?class?=?class?constructor1

          -----

          name?=?constructor1

          decl?class?=?class?constructor1

          param?#0?int

          param?#1?double

          -----

          5.獲取類的字段(域)

          找出一個類中定義了哪些數據字段也是可能的,下面的代碼就在干這個事情:


          import?java.lang.reflect.*;

          public?class?field1?{
          ???private?double?d;
          ???public?static?final?int?i?=?37;
          ???String?s?=?"testing";

          ???public?static?void?main(String?args[])?{
          ???????try?{
          ???????????Class?cls?=?Class.forName("field1");
          ???????????Field?fieldlist[]?=?cls.getDeclaredFields();
          ???????????for?(int?i?=?0;?i?<?fieldlist.length;?i++)?{
          ???????????????Field?fld?=?fieldlist[i];
          ???????????????System.out.println("name?=?"?+?fld.getName());
          ???????????????System.out.println("decl?class?=?"?+?fld.getDeclaringClass());
          ???????????????System.out.println("type?=?"?+?fld.getType());
          ???????????????int?mod?=?fld.getModifiers();
          ???????????????System.out.println("modifiers?=?"?+?Modifier.toString(mod));
          ???????????????System.out.println("-----");
          ???????????}
          ???????}?catch?(Throwable?e)?{
          ???????????System.err.println(e);
          ???????}
          ???}
          }

          這個例子和前面那個例子非常相似。例中使用了一個新東西?Modifier,它也是一個?reflection?類,用來描述字段成員的修飾語,如“private?int”。這些修飾語自身由整數描述,而且使用?Modifier.toString?來返回以“官方”順序排列的字符串描述?(如“static”在“final”之前)。這個程序的輸出是:

          name?=?d

          decl?class?=?class?field1

          type?=?double

          modifiers?=?private

          -----

          name?=?i

          decl?class?=?class?field1

          type?=?int

          modifiers?=?public?static?final

          -----

          name?=?s

          decl?class?=?class?field1

          type?=?class?java.lang.String

          modifiers?=

          -----

          和獲取方法的情況一下,獲取字段的時候也可以只取得在當前類中申明了的字段信息?(getDeclaredFields),或者也可以取得父類中定義的字段?(getFields)?。


          6.根據方法的名稱來執行方法

          文本到這里,所舉的例子無一例外都與如何獲取類的信息有關。我們也可以用?reflection?來做一些其它的事情,比如執行一個指定了名稱的方法。下面的示例演示了這一操作:

          import?java.lang.reflect.*;
          public?class?method2?{
          ???public?int?add(int?a,?int?b)?{
          ???????return?a?+?b;
          ???}
          ???public?static?void?main(String?args[])?{
          ???????try?{
          ???????????Class?cls?=?Class.forName("method2");
          ???????????Class?partypes[]?=?new?Class[2];
          ???????????partypes[0]?=?Integer.TYPE;
          ???????????partypes[1]?=?Integer.TYPE;
          ???????????Method?meth?=?cls.getMethod("add",?partypes);
          ???????????method2?methobj?=?new?method2();
          ???????????Object?arglist[]?=?new?Object[2];
          ???????????arglist[0]?=?new?Integer(37);
          ???????????arglist[1]?=?new?Integer(47);
          ???????????Object?retobj?=?meth.invoke(methobj,?arglist);
          ???????????Integer?retval?=?(Integer)?retobj;
          ???????????System.out.println(retval.intvalue());
          ???????}?catch?(Throwable?e)?{
          ???????????System.err.println(e);
          ???????}
          ???}
          }

          假如一個程序在執行的某處的時候才知道需要執行某個方法,這個方法的名稱是在程序的運行過程中指定的?(例如,JavaBean?開發環境中就會做這樣的事),那么上面的程序演示了如何做到。

          上例中,getMethod?用于查找一個具有兩個整型參數且名為?add?的方法。找到該方法并創建了相應的?Method?對象之后,在正確的對象實例中執行它。執行該方法的時候,需要提供一個參數列表,這在上例中是分別包裝了整數?37?和?47?的兩個?Integer?對象。執行方法的返回的同樣是一個?Integer?對象,它封裝了返回值?84。

          7.創建新的對象

          對于構造器,則不能像執行方法那樣進行,因為執行一個構造器就意味著創建了一個新的對象?(準確的說,創建一個對象的過程包括分配內存和構造對象)。所以,與上例最相似的例子如下:

          import?java.lang.reflect.*;

          public?class?constructor2?{
          ???public?constructor2()?{
          ???}

          ???public?constructor2(int?a,?int?b)?{
          ???????System.out.println("a?=?"?+?a?+?"?b?=?"?+?b);
          ???}

          ???public?static?void?main(String?args[])?{
          ???????try?{
          ???????????Class?cls?=?Class.forName("constructor2");
          ???????????Class?partypes[]?=?new?Class[2];
          ???????????partypes[0]?=?Integer.TYPE;
          ???????????partypes[1]?=?Integer.TYPE;
          ???????????Constructor?ct?=?cls.getConstructor(partypes);
          ???????????Object?arglist[]?=?new?Object[2];
          ???????????arglist[0]?=?new?Integer(37);
          ???????????arglist[1]?=?new?Integer(47);
          ???????????Object?retobj?=?ct.newInstance(arglist);
          ???????}?catch?(Throwable?e)?{
          ???????????System.err.println(e);
          ???????}
          ???}
          }

          根據指定的參數類型找到相應的構造函數并執行它,以創建一個新的對象實例。使用這種方法可以在程序運行時動態地創建對象,而不是在編譯的時候創建對象,這一點非常有價值。

          8.改變字段(域)的值

          reflection?的還有一個用處就是改變對象數據字段的值。reflection?可以從正在運行的程序中根據名稱找到對象的字段并改變它,下面的例子可以說明這一點:

          import?java.lang.reflect.*;

          public?class?field2?{
          ???public?double?d;

          ???public?static?void?main(String?args[])?{
          ???????try?{
          ???????????Class?cls?=?Class.forName("field2");
          ???????????Field?fld?=?cls.getField("d");
          ???????????field2?f2obj?=?new?field2();
          ???????????System.out.println("d?=?"?+?f2obj.d);
          ???????????fld.setDouble(f2obj,?12.34);
          ???????????System.out.println("d?=?"?+?f2obj.d);
          ???????}?catch?(Throwable?e)?{
          ???????????System.err.println(e);
          ???????}
          ???}
          }

          這個例子中,字段?d?的值被變為了?12.34。

          9.使用數組

          本文介紹的?reflection?的最后一種用法是創建的操作數組。數組在?Java?語言中是一種特殊的類類型,一個數組的引用可以賦給?Object?引用。觀察下面的例子看看數組是怎么工作的:

          import?java.lang.reflect.*;

          public?class?array1?{
          ???public?static?void?main(String?args[])?{
          ???????try?{
          ???????????Class?cls?=?Class.forName("java.lang.String");
          ???????????Object?arr?=?Array.newInstance(cls,?10);
          ???????????Array.set(arr,?5,?"this?is?a?test");
          ???????????String?s?=?(String)?Array.get(arr,?5);
          ???????????System.out.println(s);
          ???????}?catch?(Throwable?e)?{
          ???????????System.err.println(e);
          ???????}
          ???}
          }

          例中創建了?10?個單位長度的?String?數組,為第?5?個位置的字符串賦了值,最后將這個字符串從數組中取得并打印了出來。

          下面這段代碼提供了一個更復雜的例子:

          import?java.lang.reflect.*;

          public?class?array2?{
          ???public?static?void?main(String?args[])?{
          ???????int?dims[]?=?new?int[]{5,?10,?15};
          ???????Object?arr?=?Array.newInstance(Integer.TYPE,?dims);
          ???????Object?arrobj?=?Array.get(arr,?3);
          ???????Class?cls?=?arrobj.getClass().getComponentType();
          ???????System.out.println(cls);
          ???????arrobj?=?Array.get(arrobj,?5);
          ???????Array.setInt(arrobj,?10,?37);
          ???????int?arrcast[][][]?=?(int[][][])?arr;
          ???????System.out.println(arrcast[3][5][10]);
          ???}
          }
          例中創建了一個?5?x?10?x?15?的整型數組,并為處于?[3][5][10]?的元素賦了值為?37。注意,多維數組實際上就是數組的數組,例如,第一個?Array.get?之后,arrobj?是一個?10?x?15?的數組。進而取得其中的一個元素,即長度為?15?的數組,并使用?Array.setInt?為它的第?10?個元素賦值。

          注意創建數組時的類型是動態的,在編譯時并不知道其類型。

          posted on 2007-09-25 00:06 -274°C 閱讀(25187) 評論(2)  編輯  收藏 所屬分類: JAVA


          FeedBack:
          # re: 轉載 Java Reflection (JAVA反射)
          2007-09-27 19:24 | 千里冰封
          反射是很強大的,只要我們好好去用它  回復  更多評論
            
          # re: 轉載 Java Reflection (JAVA反射) [未登錄]
          2007-09-28 11:04 | -274°C
          @千里冰封

          哎,我項目中也沒有用到這些技術。所以只停留在初步了解的層次。該文的學習我一般是一邊看一邊敲練習代碼。有誰需要實例代碼 以及 《侯捷觀點 java反射機制》PDF 文件 的朋友嗎?  回復  更多評論
            

          常用鏈接

          留言簿(21)

          隨筆分類(265)

          隨筆檔案(242)

          相冊

          JAVA網站

          關注的Blog

          搜索

          •  

          積分與排名

          • 積分 - 916109
          • 排名 - 40

          最新評論

          主站蜘蛛池模板: 彭州市| 丽水市| 张家港市| 石渠县| 婺源县| 安丘市| 锡林郭勒盟| 贡山| 宜阳县| 资阳市| 东乌珠穆沁旗| 天柱县| 临海市| 永吉县| 横峰县| 沂源县| 杭锦后旗| 乐安县| 富锦市| 彰化县| 鹿泉市| 镇安县| 宜州市| 上虞市| 柏乡县| 永州市| 丹东市| 富顺县| 四子王旗| 清镇市| 黄梅县| 太谷县| 永靖县| 枝江市| 平罗县| 岳西县| 武夷山市| 普兰县| 疏附县| 晋宁县| 苗栗市|