java的反射(java.lang.Class 、java.lang.reflect )

學習參考  java 編程動態性文章:

反射基礎

1、使用反射的啓點總是 java.lang.Class 實例

如果您希望與預先定義的類協作,那麼Java語言提供一種直接獲得 Class 實例的簡便快捷方式:
Class clas = MyClass.class;
當您使用這一項技術時,裝入類涉及的所有工作在幕後進行。

但,如果您需要在運行時從某些外部源讀取類名,這種方法並不適合。實際上,您需要使用一個類裝載器來查找類信息。以下介紹一種方法:

try{
Class<?> class = Class.forName(name);//此方法會完成類的初始化工作(執行靜態塊)
}catch(ClassNotFoundException e){
   e.printStackTrace();
}
//如果已經裝入了類,您將得到現有的 Class 信息。如果類未被裝入,類裝入器將現在裝入並返回新創建的類實例。



2、得到Class 對象後,可以獲得類自身的信息,如:包和父類、及實現的接口、定義的構造函數和屬性、方法等信息。


用戶查找構造函數的一組反射調用:
          
Constructor<T> getConstructor(Class<?>... parameterTypes)
返回一個 Constructor 對象,它反映此 Class 對象所表示的類的指定公共構造方法。

返回一個 Constructor 對象,它反映此 Class 對象所表示的類的指定公共構造方法。parameterTypes 參數是 Class 對象的一個數組,這些 Class 對象按聲明順序標識構造方法的形參類型。
Constructor<?>[] getConstructors()
返回一個包含某些 Constructor 對象的數組,這些對象反映此 Class 對象所表示的類的所有公共構造方法。

Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
返回一個 Constructor 對象,該對象反映此 Class 對象所表示的類或接口的指定構造方法

Constructor<?>[] getDeclaredConstructors()
返回 Constructor 對象的一個數組,這些對象反映此 Class 對象表示的類聲明的所有構造方法。


這些調用都返回一個或多個 java.lang.reflect.Constructor 函數。這種 Constructor 類定義 ne wInstance 方法,它採用一組對象作爲其唯一的參數,然後返回新創建的原始類實例。

3、得到構造函數後可以利用  java.lang.reflect.Constructor類 函數創建類的實例對象

java.lang.reflect.Constructor類 的 函數:

T newInstance(Object... initargs)
使用此 Constructor 對象表示的構造方法來創建該構造方法的聲明類的新實例,並用指定的初始化參數初始化該實例。

擴展:

java編程語言還定義了一種您可以用來使用 無參數(或缺省)構造函數創建類的一個實例的特殊快捷方式。這種快捷方式嵌入到 Class 定義中,如下:

Object newInstance() -- 使用缺省函數創建新的實例


4、獲得Field  (類包含的屬性)


  • Field getField(String name) -- 獲得命名的公共字段
  • Field[] getFields() -- 獲得類的所有公共字段
  • Field getDeclaredField(String name) -- 獲得類聲明的命名的字段
  • Field[] getDeclaredFields() -- 獲得類聲明的所有字段
a、然後調用java.lang.reflect.Field 類的方法 取得/設置某個對象該屬性的值

Object get(Object obj)
返回指定對象上此 Field 表示的字段的值。

void set(Object obj, Object value)
將指定對象變量上此 Field 對象表示的字段設置爲指定的新值。
包含getXXX 和 setXXX 方法。


b、獲得屬性上的註解
<T extends Annotation>
T
getAnnotation(Class<T> annotationClass)
如果存在該元素的指定類型的註釋,則返回這些註釋,否則返回 null。

5、獲得Method


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

  •  Method[] getMethods() -- 獲得類的所有公共方法
  •   Method getDeclaredMethod(String name, Class[] params) -- 使用特寫的參    數類型,獲得類聲明的命名的方法
  • Method[] getDeclaredMethods() -- 獲得類聲明的所有方法
a、調用Method對象的invoke方法:
Object invoke(Object obj, Object... args)
對帶有指定參數的指定對象調用由此 Method 對象表示的底層方法。

這種 invoke 方法使用兩個參數,爲調用提供類實例和參數值數組。
對帶有指定參數的指定對象調用由此 Method 對象表示的底層方法。個別參數被自動解包,以便與基本形參相匹配,基本參數和引用參數都隨需服從方法調用轉換。

如果底層方法是靜態的,那麼可以忽略指定的 obj 參數。該參數可以爲 null。

如果底層方法所需的形參數爲 0,則所提供的 args 數組長度可以爲 0 或 null。

如果底層方法是實例方法,則使用動態方法查找來調用它。

案例:
    
 @Test
     public void testClassForname(){
           try {
              Class<?> callClass = Class.forName("com.lucene.Student");
              Method  method= callClass.getMethod("setName", new Class[]{String.class});
              Method  method2= callClass.getDeclaredMethod( "getName", null );//得到無參的getName()方法
              System. out.println(method.getName());
              Object object = callClass.newInstance();
               method.invoke(object, new String[]{"你好"});//調用object對象的指定方法,並傳入 參數值(String數組)
              System. out.println( method2.invoke(object, null));
          } catch (Exception e) {
               // TODO Auto-generated catch block
              e.printStackTrace();
          }
     }


反射數組

數組是Java編程語言中的對象。與所有對象一樣,它們都有類。如果您有一個數組,使用標準 getClass 方法,您可以獲得該數組的類,就象任何其它對象一樣。但是,即使您有一個數組類,您也不能直接對它進行太多的操作 -- 反射爲標準類提供的構造函數接入不能用於數組,而且數組沒有任何可接入的字段,只有基本的 java.lang.Object 方法定義用於數組對象。

數組的特殊處理使用 java.lang.reflect.Array 類提供的靜態方法的集合。該類中的方法使您能夠創建新數組,獲得數組對象的長度,讀和寫數組對象的索引值。

清單5顯示了一種重新調整現有數組大小的有效方法。它使用反射來創建相同類型的新數組,然後在返回新數組之前,在老數組中複製所有數據。

清單 5:通過反射來擴展一個數組

public Object growArray(Object array, int size) {
    Class type = array.getClass().getComponentType();
    Object grown = Array.newInstance(type, size);
    System.arraycopy(array, 0, grown, 0,
        Math.min(Array.getLength(array), size));
    return grown;
}



Class 類的方法:
Class<?> getComponentType()
返回表示數組組件類型的 Class

Array類的靜態方法:
staticObject newInstance(Class<?> componentType, int... dimensions)
創建一個具有指定的組件類型和維度的新數組。


安全性和反射

在處理反射時安全性是一個較複雜的問題。反射經常由框架型代碼使用,由於這一點,您可能希望框架能夠全面接入您的代碼,無需考慮常規的接入限制。但是,在其它情況下,不受控制的接入會帶來嚴重的安全性風險,如當代碼在不值得信任的代碼共享的環境中運行時。

由於這些互相矛盾的需求,Java編程語言定義一種多級別方法來處理反射的安全性。基本模式是對反射實施與應用於源代碼接入相同的的限制:

  • 從任意位置到類公共組件的接入
  • 類自身外部無任何到私有組件的接入
  • 受保護和打包(缺省接入)組件的有限接入

不過-至少某些時候,圍繞這些限制有一種簡單的方法。我在前面實例中使用的 Constructor 、 Field 和 Method 類都擴展了一個普通的基本類--&#160 java.lang.reflect.AccessibleObject 類。該類定義一種 setAccessible 方法,使您能夠啓動或關閉對這些類中其中一個類的實例的接入檢測。唯一的問題在於如果使用了安全性管理器,它將檢測正在關閉接入檢測的代碼是否許可了這樣做。如果未許可,安全性管理器拋出一個例外。

清單6展示了一個程序,在 清單 1TwoString 類的一個實例上使用反射來顯示安全性正在運行:

清單 6:反射安全性正在運行

public class ReflectSecurity {
    public static void main(String[] args) {
        try {
            TwoString ts = new TwoString("a", "b");
            Field field = clas.getDeclaredField("m_s1");
//          field.setAccessible(true);
            System.out.println("Retrieved value is " +
                field.get(inst));
        } catch (Exception ex) {
            ex.printStackTrace(System.out);
        }
    }
}


如果您編譯了這一程序,不使用任何特定參數直接從命令行運行,它將在 field.get(inst) 調用中拋出一個 IllegalAccessException 。如果您未註釋 field.setAccessible(true) 代碼行,那麼重新編譯並重新運行該代碼,它將取得成功。最後,如果您在命令行添加了JVM參數 -Djava.security.manager 以實現安全性管理器,它將再次失敗,除非您定義了 ReflectSecurity 類的許可權限。


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