Java中的反射
本文為反射的基礎知識部分。
能夠分析類能力的程序被稱為反射(reflective)。
反射機制允許程序在運行時取得任何一個已知名稱的class的內部信息,容許程序在運行時加載、探知、使用編譯期間未知的class。即Java的反射機制可以加載一個運行時才得知名稱的class,獲得其完整結構。
一.Class類
在程序運行期間,Java運行時系統始終為所有的對象維護一個被稱為運行時的類型標識。
這個信息保存着每個對象所屬的類足跡。虛擬機利用運行時信息選擇相應的方法執行。
然而,可以通過專門的Java類訪問這些信息。保存這些信息的類稱為Class.
(注:新版本為Class<T>,本文中經常為了方便只寫Class)
API: java.lang.Class
(1.7)http://docs.oracle.com/javase/7/docs/api/index.html
獲取類的Class對象的方法:
調用getClass() (Object類中的getClass()方法返回一個Class類型的實例) |
Boolean var1 = true;Class<?> classType2 = var1.getClass();System.out.println(classType2);輸出:class java.lang.Boolean |
運用T.class 語法 (T是任意的Java類型) |
Class<?> classType4 = Boolean.class;System.out.println(classType4);輸出:class java.lang.Boolean |
運用static method Class.forName() (使用時應該提供異常處理器) |
Class<?> classType5 = Class.forName("java.lang.Boolean");System.out.println(classType5);輸出:class java.lang.Boolean |
運用primitive wrapper classes的TYPE 語法 (這里返回的是原生類型,和Boolean.class返回的不同) |
Class<?> classType3 = Boolean.TYPE;System.out.println(classType3);輸出:boolean |
一個Class對象實際上表示的是一個類型,而這個類型未必一定是一種類。
例如,int不是類,但int.class是一個Class類型的對象。
虛擬機為每個類型管理一個Class對象。因此,可以用==運算符實現兩個類對象比較的操作。
最常用的Class方法:
方法 |
說明 |
例子 |
getName()
|
返回類的名字 |
String.class.getName(); 返回: "java.lang.String" |
newInstance()
|
快速地創建一個類的實例 (調用默認構造器,如果該類沒有默認構造器,拋出異常) (如果要為構造器提供參數,使用java.lang.reflect.Constructor中的newInstance方法) |
String s = "java.util.Date"; Object m = Class.forName(s).newInstance(); |
getSuperclass() |
返回超類 |
|
getFields()getMethods()getConstructors()(還有帶字符串參數,給定名稱的形式)
|
分別返回類支持的public域、方法和構造器數組,其中包括超類的公有成員 |
|
getDeclaredFields() getDeclaredMethods() getDeclaredConstructors() (還有給定名稱的形式)
|
分別返回類中聲明的全部域、方法和構造器數組。其中包括私有和保護成員,但不包括超類的成員 |
|
二.java.lang.reflect包
上文中提到的Class類中的getFields()、getMethods()、getConstructors()方法的返回值都是特定類型(Field、Method、Constructor類)的數組類型。
Field、Method、Constructor類,分別用於描述類的域、方法和構造器,都在java.lang.reflect包中。
java.lang.reflect包提供反射相關的API,1.7.0的reflect包如下圖:
(http://docs.oracle.com/javase/7/docs/api/java/lang/reflect/package-frame.html)
2.1使用反射分析類
如前所述,Field、Method、Constructor類,分別用於描述類的域、方法和構造器。另外java.lang.reflect包中的Modifier類可以分析訪問修飾符。那么用它們就可以分析類。
分析類常用的方法:
類 | 方法 | 作用 |
FieldMethodConstructor | Class getDeclaringClass() | 返回一個用於描述類中定義的構造器、方法或域的Class對象 |
String getName() | 返回相應條目的名稱 | |
int getModifiers() | 返回整型數值,用不同的位開關描述訪問修飾符的使用狀況 | |
Method Constructor |
Class[] getExceptionTypes() |
返回一個用於描述方法拋出的異常類型的Class對象數組 |
Class[] getParameterTypes() |
返回一個用於描述參數類型的Class對象數組 |
|
Field |
Class getType() |
用於返回描述域所屬類型的Class類型對象 |
Modifier |
static String toString(int modifiers) |
返回對應modifiers位設置的修飾符的字符串表示 |
Static boolean isXXX(int modifiers) |
檢測方法名中對應的修飾符在modifiers中的值 |
2.2使用反射分析對象
2.1節已經知道如何查看任意對象的數據域名稱和類型,在本節中,將進一步查看數據域的實際內容。利用反射機制可以查看在編譯時還不清楚的對象域。
查看對象域的關鍵方法是Field類中的get方法。
如果f是一個Field類型的對象(例如,通過getField(String name)得到的對象),obj是某個包含f域的類的對象,那么f.get(obj)將返回一個對象,其值為obj的f域的當前值。
可以獲取就可以設置,調用f.set(obj,value)可以將obj對象的f域設置成新值。
訪問權限問題
上面所說的例子中,如果該域為一個私有域,get方法將會拋出一個異常。
這是因為反射機制的默認行為受限於Java的訪問控制,比如,除非擁有訪問權限,否則Java安全機制允許查看任意對象有哪些域,而不允許讀它們的值。
然而如果一個Java程序沒有受到安全管理器的控制,就可以覆蓋訪問控制。為了達到這個目的,就需要調用Field、Method、Constructor對象的setAccessible方法。
Field、Method、Constructor類都派生自AccessibleObject.
AccessibleObject常用方法:
函數 |
作用 |
void setAccessible(boolean flag) |
為反射對象設置可訪問標志,flag為true表明屏蔽Java語言的訪問檢查,使得對象的私有屬性也可以被查詢和設置 |
boolean isAccessible() |
返回反射對象的可訪問標志的值 |
static void setAccessible(AccessibleObject[] array, boolean flag) |
一種設置對象數組可訪問標志的快捷方法 |
三.資源
本文主要參考《JAVA 2核心技術 卷Ⅰ》第五章有關內容。
書中代碼下載地址:http://horstmann.com/corejava.html
API文檔地址http://docs.oracle.com/javase/7/docs/api/index.html
另外關於反射,比較有用的幾個博客鏈接:
http://www.blogjava.net/jialing/archive/2006/08/24/JavaReflectionCookbook1.html
http://www.cnblogs.com/rollenholt/archive/2011/09/02/2163758.html
http://www.cnblogs.com/Quincy/archive/2011/06/19/2084557.html