反射基礎
1、使用反射的啓點總是 java.lang.Class
實例
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類 函數創建類的實例對象
T |
newInstance(Object... initargs) 使用此 Constructor 對象表示的構造方法來創建該構造方法的聲明類的新實例,並用指定的初始化參數初始化該實例。 |
java編程語言還定義了一種您可以用來使用 無參數(或缺省)構造函數創建類的一個實例的特殊快捷方式。這種快捷方式嵌入到 Class
定義中,如下:
Object newInstance()
--
使用缺省函數創建新的實例
4、獲得Field (類包含的屬性)
-
Field getField(String name)
-- 獲得命名的公共字段 -
Field[] getFields()
-- 獲得類的所有公共字段 -
Field getDeclaredField(String name)
-- 獲得類聲明的命名的字段 -
Field[] getDeclaredFields()
-- 獲得類聲明的所有字段
getXXX
和 setXXX
方法。
|
getAnnotation(Class<T> annotationClass) 如果存在該元素的指定類型的註釋,則返回這些註釋,否則返回 null。 |
5、獲得Method
-
Method getMethod(String name, Class[] params)
-- 使用特定的參數類型,獲得命名的公共方法 -
Method[] getMethods()
-- 獲得類的所有公共方法Method getDeclaredMethod(String name, Class[] params)
-- 使用特寫的參 數類型,獲得類聲明的命名的方法-
Method[] getDeclaredMethods()
-- 獲得類聲明的所有方法
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<?> |
getComponentType() 返回表示數組組件類型的 Class 。 |
staticObject |
newInstance(Class<?> componentType, int... dimensions) 創建一個具有指定的組件類型和維度的新數組。 |
安全性和反射
在處理反射時安全性是一個較複雜的問題。反射經常由框架型代碼使用,由於這一點,您可能希望框架能夠全面接入您的代碼,無需考慮常規的接入限制。但是,在其它情況下,不受控制的接入會帶來嚴重的安全性風險,如當代碼在不值得信任的代碼共享的環境中運行時。
由於這些互相矛盾的需求,Java編程語言定義一種多級別方法來處理反射的安全性。基本模式是對反射實施與應用於源代碼接入相同的的限制:
- 從任意位置到類公共組件的接入
- 類自身外部無任何到私有組件的接入
- 受保護和打包(缺省接入)組件的有限接入
不過-至少某些時候,圍繞這些限制有一種簡單的方法。我在前面實例中使用的 Constructor
、 Field
和 Method
類都擴展了一個普通的基本類--  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
類的許可權限。