java 反射入門

一、反射的概念 :

    反射的概念是由Smith在1982年首次提出的,主要是指程序可以訪問、檢測和修改它本身狀態或行爲的一種能力。這一概念的提出很快引發了計算機科學領 域關於應用反射性的研究。它首先被程序語言的設計領域所採用,並在Lisp和麪向對象方面取得了成績。其中LEAD/LEAD++ 、OpenC++ 、 MetaXa和OpenJava等就是基於反射機制的語言。最近,反射機制也被應用到了視窗系統、操作系統 和文件系統中。 

    反射本身並不是一個新概念,它可能會使我們聯想到光學中的反射概念,儘管計算機科學賦予了反射概念新的含義,但是,從現象上來說,它們確實有某些相通之 處,這些有助於我們的理解。在計算機科學領域,反射是指一類應用,它們能夠自描述和自控制。也就是說,這類應用通過採用某種機制來實現對自己行爲的描述 (self-representation)和監測(examination),並能根據自身行爲的狀態和結果,調整或修改應用所描述行爲的狀態和相關的 語義。可以看出,同一般的反射概念相比,計算機科學領域的反射不單單指反射本身,還包括對反射結果所採取的措施。所有采用反射機制的系統(即反射系統)都 希望使系統的實現更開放。可以說,實現了反射機制的系統都具有開放性,但具有開放性的系統並不一定採用了反射機制,開放性是反射系統的必要條件。一般來 說,反射系統除了滿足開放性條件外還必須滿足原因連接(Causally-connected)。所謂原因連接是指對反射系統自描述的改變能夠立即反映到 系統底層的實際狀態和行爲上的情況,反之亦然。開放性和原因連接是反射系統的兩大基本要素。

    Java中,反射是一種強大的工具。它使您能夠創建靈活的代碼,這些代碼可以在運行時裝配,無需在組件之間進行源代表鏈接。反射允許我們在編寫與執行時, 使我們的程序代碼能夠接入裝載到JVM中的類的內部信息,而不是源代碼中選定的類協作的代碼。這使反射成爲構建靈活的應用的主要工具。但需注意的是:如果 使用不當,反射的成本很高。

二、Java中的類反射:

Reflection 是 Java 程序開發語言的特徵之一,它允許運行中的 Java 程序對自身進行檢查,或 者說“自審”,並能直接操作程序的內部屬性。Java 的這一能力在實際應用中也許用得不是很多,但是在其它的程序設計語言中根本就不存在這一特性。例 如,Pascal、C 或者 C++ 中就沒有辦法在程序中獲得函數定義相關的信息。

1 .檢測類:

1.1 reflection 的工作機制

用一句比較白的話來概括,反射就是讓你可以通過名稱來得到對象 ( 類,屬性,方法 ) 的技術。例如我們可以通過類名來生成一個類的實例;知道了方法名,就可以調用這個方法;知道了屬性名就可以訪問這個屬性的值。

還是寫兩個例子讓大家更直觀的瞭解反射的使用方法:


// 通過類名來構造一個類的實例
Class cls_str = Class.forName( "java.lang.String" );
// 上面這句很眼熟,因爲使用過 JDBC 訪問數據庫的人都用過 J
Object str = cls_str.newInstance();
// 相當於 String str = new String();


// 通過方法名來調用一個方法
String methodName = "length" ;
Method m = cls_str.getMethod(methodName, null );
System.out.println( "length is " + m.invoke(str, null ));
// 相當於 System.out.println(str.length());

考慮下面這個簡單的例子,讓我們看看 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

它的結果輸出爲:

public int java.lang.String.hashCode()
public int java.lang.String.compareTo(java.lang.String)
public int java.lang.String.compareTo(java.lang.Object)

****************

public static java.lang.String java.lang.String.valueOf(char)

 

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

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

1.2 Java 類反射中的主要方法

對於以下三類組件中的任何一類來說 -- 構造函數、字段和方法 -- java.lang.Class 提供四種獨立的反射調用,以不同的方式來獲得信息。調用都遵循一種標準格式。以下是用於查找構造函數的一組反射調用:

l         Constructor getConstructor(Class[] params) -- 獲得使用特殊的參數類型的公共構造函數,

l         Constructor[] getConstructors() -- 獲得類的所有公共構造函數

l         Constructor getDeclaredConstructor(Class[] params) -- 獲得使用特定參數類型的構造函數(與接入級別無關)

l         Constructor[] getDeclaredConstructors() -- 獲得類的所有構造函數(與接入級別無關) 

獲得字段信息的Class 反射調用不同於那些用於接入構造函數的調用,在參數類型數組中使用了字段名:

l         Field getField(String name) -- 獲得命名的公共字段

l         Field[] getFields() -- 獲得類的所有公共字段

l         Field getDeclaredField(String name) -- 獲得類聲明的命名的字段

l         Field[] getDeclaredFields() -- 獲得類聲明的所有字段 

用於獲得方法信息函數:

l         Method getMethod(String name, Class[] params) -- 使用特定的參數類型,獲得命名的公共方法

l         Method[] getMethods() -- 獲得類的所有公共方法

l         Method getDeclaredMethod(String name, Class[] params) -- 使用特寫的參數類型,獲得類聲明的命名的方法

l         Method[] getDeclaredMethods() -- 獲得類聲明的所有方法 

 

1.3 開始使用 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 中定義的第一個方法的原型。

2 .處理對象:

如果要作一個開發工具像debugger之類的,你必須能發現filed values,以下是三個步驟:

a.創建一個Class對象
b.通過getField 創建一個Field對象
c.調用Field.getXXX(Object)方法(XXX是Int,Float等,如果是對象就省略;Object是指實例).

例如:
import java.lang.reflect.*;
import java.awt.*;

class SampleGet {

   public static void main(String[] args) {
      Rectangle r = new Rectangle(100, 325);
      printHeight(r);

   }

   static void printHeight(Rectangle r) {
      Field heightField;
      Integer heightValue;
      Class c = r.getClass();
      try {
        heightField = c.getField("height");
        heightValue = (Integer) heightField.get(r);
        System.out.println("Height: " + heightValue.toString());
      } catch (NoSuchFieldException e) {
          System.out.println(e);
      } catch (SecurityException e) {
          System.out.println(e);
      } catch (IllegalAccessException e) {
          System.out.println(e);
      }
   }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章