隨筆 - 10  文章 - 16  trackbacks - 0
          <2025年6月>
          25262728293031
          1234567
          891011121314
          15161718192021
          22232425262728
          293012345

          常用鏈接

          留言簿(1)

          隨筆分類

          文章分類

          我的好友

          搜索

          •  

          最新隨筆

          最新評論

          閱讀排行榜

          評論排行榜

          作者:yonnie?文章來源:http://yonnie.blog.hexun.com/

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

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

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

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

          ?1 import ?java.lang.reflect. * ;
          ?2

          ?3 public ? class ?DumpMethods?
          {
          ?4 ???? public ? static ? void ?main(String?args[])?
          {
          ?5 ???????? try ?
          {
          ?6 ????????????Class?c? = ?Class.forName(args[ 0
          ]);
          ?7 ????????????Method?m[]? =
          ?c.getDeclaredMethods();
          ?8 ???????????? for ?( int ?i? = ? 0 ;?i? < ?m.length;?i ++
          )
          ?9
          ????????????????System.out.println(m[i].toString());
          10 ????????}
          ? catch ?(Throwable?e)? {
          11
          ????????????System.err.println(e);
          12 ????????}

          13 ????}

          14 }

          15

          按如下語句執行:
          java?DumpMethods?java.util.Stack

          它的結果輸出為:
          public ? synchronized ?java.lang.Object?java.util.Stack.pop()
          public
          ?java.lang.Object?java.util.Stack.push(java.lang.Object)
          public ? boolean
          ?java.util.Stack.empty()
          public ? synchronized
          ?java.lang.Object?java.util.Stack.peek()
          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 2006-10-30 12:48 Toez 閱讀(203) 評論(0)  編輯  收藏 所屬分類: Java

          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 平乐县| 祁连县| 斗六市| 彝良县| 烟台市| 长子县| 大新县| 乐清市| 三河市| 永定县| 民权县| 波密县| 邯郸市| 方正县| 延庆县| 饶阳县| 庄河市| 苍南县| 北辰区| 和龙市| 安新县| 德惠市| 万荣县| 喜德县| 沅江市| 濉溪县| 博湖县| 南雄市| 肇东市| 浦县| 津南区| 新泰市| 紫云| 福建省| 南川市| 镇康县| 哈巴河县| 曲阜市| 凯里市| 夹江县| 曲松县|