基本概念
在Java運行時環境中,對於任意一個類,能否知道這個類有哪些屬性和方法?對於任意一個對象,能否調用它的任意一個方法?
答案是肯定的。
這種動態獲取類的信息以及動態調用對象的方法的功能來自於Java語言的反射(Reflection)機制。
JAVA反射機制是指:在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意一個方法;這種動態獲取的以及動態調用對象的方法的功能稱爲java語言的反射機制。換句話說,Java程序可以加載一個運行時才得知名稱的class,獲悉其完整構造(但不包括methods定義),並生成其對象實體、或對其fields設值、或喚起其methods。
Java反射機制主要提供了以下功能:
1.在運行時判斷任意一個對象所屬的類。
2.在運行時構造任意一個類的對象。
3.在運行時判斷任意一個類所具有的成員變量和方法。
4.在運行時調用任意一個對象的方法。
Reflection是Java被視爲動態(或準動態)語言的一個關鍵性質。
這個機制允許程序在運行時透過Reflection APIs取得任何一個已知名稱的class的內部信息。
包括其modifiers(諸如public、static等)、 superclass(例如Object)、實現了的 interfaces (例如Serializable)、也包括其fields和methods的所有信息,並可於運行時改變fields內容或調用methods。
動態語言
動態語言的定義“程序運行時,允許改變程序結構或者變量類型,這種語言稱爲動態語言”。
從這個觀點看,Perl,Python,Ruby是動態語言,C++,Java,C#不是動態語言。
儘管在這樣的定義與分類下Java不是動態語言,它卻有着一個非常突出的動態相關機制:Reflection。這個字的意思是:反射、映像、倒影,用在Java身上指的是我們可以於運行時加載、探知、使用編譯期間完全未知的classes。
換句話說,Java程序可以加載一個運行時才得知名稱的class,獲悉其完整構造(但不包括methods定義),並生成其對象實體、或對其fields設值、或喚起其methods。
這種“看透”class的能力(the ability of the program to examine itself)被稱爲introspection(內省、內觀、反省)。Reflection和introspection是常被並提的兩個術語。
Java Reflection API簡介
在JDK中,主要由以下類來實現Java反射機制,這些類(除了第一個)都位於java.lang.reflect包中
Class類:代表一個類,位於java.lang包下。
Field類:代表類的成員變量(成員變量也稱爲類的屬性)。
Method類:代表類的方法。
Constructor類:代表類的構造方法。
Array類:提供了動態創建數組,以及訪問數組的元素的靜態方法。
java反射機制的實現主要由三個類來主導:它們分別是Class、Field、Method;
1. Class:
java在編譯和運行時,會將需要被編譯和運行的所有類加載進類加載器,每個類被加載之後,系統就會爲該類生成一個對應的Class對象,通過該Class對象就可以訪問到java虛擬機中的這個類,進而在運行時對這個被訪問的類進行信息的獲取和處理(當然,不管被訪問的這個類裏的信息是否私有的);通俗的講,Class對象間接代表了它對應的類,通過這個Class對象,我們就可以大刀闊斧地去執行反射機制的實現;
獲取Class對象主要有三種方式:
1). 調用Class類的forName(String name)靜態方法,參數name爲Class對應的類的全名(包括包名);
比如我們要創建Gesture這個類對應的Class對象:Class<Gesture> mClass = Class.forName("android.gesture.Gesture");
android.gesture爲Gesture的包名,Class<Gesture>中的Gesture表示得到的是Gesture類型對應的Class對象,<>中的Gesture也可爲通配符?表示,如:Class<?>mClass = Class.forName("android.gesture.Gesture");
2). 調用類的class屬性得到類對應的Class對象。如:Class<?>mClass = Gesture.class; (一般建議用這種方式得到Class對象)
3).調用類的實例化對象的getClass()方法。特別說明的是,getClass()是java類的始祖Object類的方法,所以,所有java對象都可以調用該方法;如mGesture是Gesture類型的對象,Class<?> mClass = mGesture.getClass()得到的是Gesture類對應的Class對象
那麼在得到對應類的Class對象對應後,我們就可以通過該Class對象得到它所對應的類的一些信息,比如該類的構造函數、成員(屬性)、方法(函數);
Class類提供的相關接口介紹:(注:在表中,Class對象對應的類,姑且稱爲目標類)
接口 | 返回類型 | 接口功能實現 |
getPackage() |
Package | 得到目標類的包名對應的Package對象 |
getCanonicalName() |
String | 得到目標類的全名(包名+類名) |
getName() | String | 同getCanonicalName() |
getClassLoader() |
ClassLoader |
得到加載目標類的ClassLoader對象 |
getClasses() |
Class<?>[] | 得到目標類中的所有的public內部類以及public內部接口所對應的Class對象 |
getDeclaredClasses() |
Class<?>[] |
同getClasses(),但不侷限於public修飾,只要是目標類中聲明的內部類和接口均可 |
getConstructors() |
Constructor<?>[] |
得到目標類的所有public構造函數對應的Constructor對象 |
getDeclaredConstructors() |
Constructor<?>[] |
同getConstructors(),但不侷限於public修飾,只要是目標類中聲明的構造函數均可 |
getField(String arg) |
Field | 得到目標類中指定的某個public屬性對應的Field對象 |
getDeclaredField(String arg) |
Field | 同getField,但不侷限於public修飾,只要是目標類中聲明的屬性均可 |
getFields() |
Field[] | 得到目標類中所有的public屬性對應的Field對象 |
getDeclaredFields() |
Field[] | 同getFields(),但不侷限於public修飾的屬性 |
getMethod(String arg0, Class<?>... arg1) |
method | 得到目標類中指定的某個public方法對應的Method對象 |
getDeclaredMethod(String arg0, Class<?>... arg1) |
Method | 同getMethod,但不侷限於public修飾的方法 |
getMethods() |
Method[] | 得到目標類中所有的public方法對應的Method對象 |
getDeclaredMethods() |
Method[] | 同getMethods(),但不侷限於public修飾的方法 |
getEnclosingClass() |
Class | 得到目標類所在的外圍類的Class對象 |
getGenericInterfaces() |
Type[] | 得到目標類實現的接口對應的Type對象 |
getGenericSuperclass() |
Type | 得到目標類繼承的父類所對應的Type對象 |
getInterfaces() |
Class<?>[] | 得到目標類實現的接口所對應的Class對象 |
getSuperclass() |
Class | 得到目標類繼承的父類所對應的Class對象 |
isMemberClass() |
boolean | 目標類是否爲成員類 |
cisAnonymousClass() |
boolean | 目標類是否爲匿名類 |
2.Field:
我們知道一般類裏包含有屬性(成員)和方法(函數),竟然Class是描述類的信息,那麼類其它部分應該會對應有描述它們的東東,而Field類型的對象就是描述Class對象對應類的出現包括public、protected、private屬性);一個Field對象對應描述一個類的屬性;
通過上文對Class的介紹,我們知道Class提供了四種接口函數可以得到對應屬性的Field:
1). getField(String name):返回類型爲Field,name爲類中的屬性名,得到的是描述類中的一個public屬性對應的Field對象;如Field mField =mClass.getField("mGestureID") 得到的是Gesture類中的一個public屬性mGestureID對應的Field對象;
2). getFields():返回類型爲Field類型數組,得到的是描述類中的所有public屬性對應的所有Field對象;
3). getDeclaredField(String name):同getField(String name),只不過得到的Field對象描述的不只是public屬性,
還包括protected、private屬性,也是說只要是在類中聲明的屬性;
4). getDeclaredFields():getFields(),得到的是描述類中聲明的所有屬性(public、protected、private)對應的Field對象;
Field類的相關函數接口介紹:
Field類提供的相關接口介紹:(注:在表中,Field對象對應的屬性,姑且稱爲目標屬性)
接口 | 返回類型 | 接口功能實現 |
setAccessible(boolean flag) | void | 參數爲true,只要是在類中聲明的目標屬性均可訪問,爲false,只有public目標屬性可訪問 |
set(Object object, Object value) | void | 給目標屬性設置值(private、protected屬性均不能訪問,但可以通過先調用setAccessible(true)實現訪問),第一個參數爲目標屬性所在類的對象,第二個參數爲傳入的值 |
get(Object object) | Object | 得到目標屬性的值(private、protected屬性均不能訪問,但可以通過調用setAccessible(true)實現訪問),參數爲目標屬性所在類的對象 |
setBoolean(Object object, boolean value) | void | 同set(Object object, Object value),只不過操作的數據類型爲boolean |
getBoolean(Object object) | boolean | 同get(Object object),只不過得到的數據類型爲boolean |
setByte(Object object, boolean value) | void | 同set(Object object, Object value),只不過操作的數據類型爲byte |
getByte(Object object) | byte | 同get(Object object),只不過得到的數據類型爲byte |
setShort(Object object, boolean value) | void | 同set(Object object, Object value),只不過操作的數據類型爲short |
getShort(Object object) | short | 同get(Object object),只不過得到的數據類型爲short |
setInt(Object object, boolean value) | void | 同set(Object object, Object value),只不過操作的數據類型爲int |
getInt(Object object) | int | 同get(Object object),只不過得到的數據類型爲int |
setLong(Object object, boolean value) | void | 同set(Object object, Object value),只不過操作的數據類型爲long |
getLong(Object object) | long | 同get(Object object),只不過得到的數據類型爲long |
setFloat(Object object, boolean value) | void | 同set(Object object, Object value),只不過操作的數據類型爲float |
getFloat(Object object) | float | 同get(Object object),只不過得到的數據類型爲float |
setDouble(Object object, boolean value) | void | 同set(Object object, Object value),只不過操作的數據類型爲double |
getDouble(Object object) | double | 同get(Object object),只不過得到的數據類型爲double |
setChar(Object object, boolean value) | void | 同set(Object object, Object value),只不過操作的數據類型爲char |
getChar(Object object) | char | 同get(Object object),只不過得到的數據類型爲char |
getName() | String | 得到目標屬性的名字,不侷限於private修飾符,只要是類中聲明的屬性 |
getGenericType() | Type | 得到目標屬性的類型,不侷限於private修飾符 |
getType() | Class<?> | 得到目標屬性的類型對應的Class對象 |
getModifiers() | int | 得到目標屬性的修飾符值(private爲2、protected爲4、public爲1、static爲8、final爲16) |
getDeclaringClass() | Class<?> | 得到目標屬性所在類對應的Class對象 |
下面就以一個示例代碼來驗證Field表中的函數接口的實現,如下:
1). FieldBeReflected.java(被反射的類)
- package com.stevenhu.field;
- public class FieldBeReflected
- {
- private static String name;
- private static String name1;
- private boolean mBoolean = true;
- private final byte mByte = 111;
- private static final short mShort = 22;
- protected static int mInt;
- protected static long mLong;
- protected static float mFloat;
- protected static double mDouble;
- public static char mChar;
- }
2). ReflectField.java(執行反射調用的類)
- package com.stevenhu.reflection.test;
- import java.lang.reflect.Field;
- import java.lang.reflect.InvocationTargetException;
- import java.lang.reflect.Method;
- import java.lang.reflect.Type;
- import com.stevenhu.field.FieldBeReflected;
- public class ReflectField
- {
- public static void main(String[] args)
- {
- /*1.Class<?> clazz = Class.forName("com.stevenhu.field.FieldBeReflected");
- *2.FieldBeReflected mFieldBeReflected = new FieldBeReflected();
- * Class<?> clazz = mFieldBeReflected.getClass();
- */
- Class<?> clazz = FieldBeReflected.class;
- try {
- Field fName = clazz.getDeclaredField("name");
- Field fBoolean = clazz.getDeclaredField("mBoolean");
- Field fByte = clazz.getDeclaredField("mByte");
- Field fShort = clazz.getDeclaredField("mShort");
- Field fInt = clazz.getDeclaredField("mInt");
- Field fLong = clazz.getDeclaredField("mLong");
- Field fFloat = clazz.getDeclaredField("mFloat");
- Field fDouble = clazz.getDeclaredField("mDouble");
- Field fChar = clazz.getDeclaredField("mChar");
- /*
- * 參數爲true,只要是在類中聲明的目標屬性均可訪問,
- * 爲false,則被反射類和反射類在同一個包中時,private目標屬性不可訪問,
- * 不在同一個包中時,private、protected目標屬性均不可訪問
- */
- fName.setAccessible(true);
- /*給目標屬性設置值(private屬性不能訪問,但可以通過先調用setAccessible(true)實現訪問),
- * 由於ReflectField類中的name屬性是靜態的(static),所以方法的第一個實參傳入的是
- * 目標屬性所在類對應的Class對象clazz,也可以是類的實例clazz.newInstance();
- */
- fName.set(clazz, "reflection");
- //得到目標屬性的值(private屬性不能訪問,但可以通過調用setAccessible(true)實現訪問)
- String name = (String) fName.get(clazz);
- System.out.println(name);
- fBoolean.setAccessible(true);
- /*得到目標屬性的布爾值,由於ReflectField類中的mBoolean屬性是非靜態的,
- * 所以此處的傳入實參爲目標屬性所在類的實例clazz.newInstance()
- */
- boolean mBoolean = fBoolean.getBoolean(clazz.newInstance());
- System.out.println(mBoolean);
- fByte.setAccessible(true);
- //得到目標屬性的Byte類型值
- byte mByte = fByte.getByte(clazz.newInstance());
- System.out.println(mByte);
- fShort.setAccessible(true);
- //得到目標屬性的short整型值
- short mShort = fShort.getShort(clazz);
- System.out.println(mShort);
- fInt.setAccessible(true);
- //給目標屬性設置整型值
- fInt.setInt(clazz, 222);
- //得到目標屬性的整型值
- int mInt = fInt.getInt(clazz);
- System.out.println(mInt);
- fLong.setAccessible(true);
- //給目標屬性設置Long整型值
- fLong.setLong(clazz, 2222);
- //得到目標屬性的Long整型值
- Long mLong = fLong.getLong(clazz);
- System.out.println(mLong);
- fFloat.setAccessible(true);
- //給目標屬性設置float類型值
- fFloat.setFloat(clazz, 22222);
- //得到目標屬性的float類型值
- float mFloat = fFloat.getFloat(clazz);
- System.out.println(mFloat);
- fDouble.setAccessible(true);
- //給目標屬性設置double類型值
- fDouble.setDouble(clazz, 222.222);
- //得到目標屬性的double類型值
- double mDouble = fDouble.getDouble(clazz);
- System.out.println(mDouble);
- //給目標屬性設置字符值(private、protected屬性不能訪問)
- fChar.setChar(clazz, 'a');
- //得到目標屬性的字符值(private、protected屬性不能訪問)
- char mChar = fChar.getChar(clazz);
- System.out.println(mChar);
- //目標屬性的名字,不侷限於修飾符,只要是類中聲明的屬性
- String name1 = fName.getName();
- System.out.println(name1);
- //目標屬性的類型,不侷限於修飾符
- Type type = fName.getGenericType();
- System.out.println(type);
- //目標屬性的類型對應的Class對象
- Class<?> clazz1 = fName.getType();
- System.out.println(clazz1);
- //目標屬性所在類對應的Class對象
- Class<?> clazz2 = fName.getDeclaringClass();
- System.out.println(clazz2);
- //目標屬性的權限修飾值(private爲2、protected爲4、public爲1)
- int modifier = fName.getModifiers();
- int modifier1 = fByte.getModifiers();
- int modifier2 = fShort.getModifiers();
- System.out.println(modifier);
- System.out.println(modifier1);
- System.out.println(modifier2);
- System.out.println(fName.isAccessible());
- System.out.println(fChar.isAccessible());
- } catch (NoSuchFieldException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (SecurityException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (IllegalArgumentException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (IllegalAccessException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (InstantiationException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
3. Method:
同Fiel一樣,一個Method對象對應描述一個類的方法;
Class對象也提供了四種接口函數得到對應方法的Method對象,如下:
1). getMethod(String name, Class<?>... parameterTypes):返回類型爲Method,第一個參數name爲類中的方法名,第二個參數爲可變參數,傳入的是參數類型對應的Class對象(方法的參數可能爲多個的情況);該函數得到的是描述類中的一個public方法對應的Method對象;
2). getMethods():返回類型爲Method類型數組,得到的是描述類中的所有public方法對應的Method對象;
3). Method getDeclaredMethod(String name, Class<?>... parameterTypes):同getMethod(String name, Class<?>... parameterTypes),只不過得到的Method對象描述的不只是public方法, 還包括
protected、private方法,也是說只要是在類中聲明的方法;
4). getDeclaredMethods():getMethods(),得到的是描述類中聲明的所有方法(public、protected、private)對應的FMethod對象;
Method類的相關函數接口介紹:
Method類提供的相關接口介紹:(注:在表中,Method對象對應的方法,姑且稱爲目標方法)
接口 | 返回類型 | 接口功能實現 |
setAccessible(boolean flag) | void | 參數爲true,只要是在類中聲明的目標方法均可訪問,爲false,只有public目標屬性可訪問 |
invoke(Object receiver, Object... args) | Object | 動態執行調用目標方法,第一個參數爲Class對象或者類的實例,第二個參數爲可變實參的對象(多個實參) |
getDeclaringClass() | Class<?> | 得到目標方法所在類對應的Class對象 |
getExceptionTypes() | Class<?> | 得到目標方法拋出的異常類型對應的Class對象 |
getGenericExceptionTypes() | Type[] | 得到目標方法拋出的異常類型對應的Type對象 |
getReturnType() | Class<?> | 得到目標方法返回類型對應的Class對象 |
getGenericReturnType() | Type | 得到目標方法返回類型對應的Type對象 |
getParameterTypes() | Class<?>[] | 得到目標方法各參數類型對應的Class對象 |
getGenericParameterTypes() | Type[] | 得到目標方法各參數類型對應的Type對象 |
getModifiers() | int | 得到目標方法修飾符的值 |
getName() | String | 得到目標方法的名字 |
下面就以一個示例代碼來驗證Method表中的函數接口的實現,如下:
1). MethodBeReflected.java(被反射的類)
- package com.stevenhu.method;
- public class MethodBeReflected
- {
- private static String mName;
- private static int mAge;
- private static float mWeight;
- private String getmName()
- {
- return mName;
- }
- protected void setmName(String mName)
- {
- this.mName = mName;
- }
- protected static int getmAge()
- {
- return mAge;
- }
- private static void setmAge(int age)
- {
- mAge = age;
- }
- private float getmWeight() throws Exception, NoSuchMethodException, SecurityException
- {
- return mWeight;
- }
- protected void setmWeight(float mWeight)
- {
- this.mWeight = mWeight;
- }
- private void setAllValues(String name, int age, float weight)
- {
- this.mName = name;
- this.mAge = age;
- this.mWeight = weight;
- }
- }
2)ReflectMethod.java(執行反射的類)
- package com.stevenhu.reflection.test;
- import java.lang.reflect.InvocationTargetException;
- import java.lang.reflect.Method;
- import java.lang.reflect.Type;
- import com.stevenhu.method.MethodBeReflected;
- public class ReflectMethod
- {
- /**
- * @param args
- */
- public static void main(String[] args)
- {
- // TODO Auto-generated method stub
- Class<?> clazz = MethodBeReflected.class;
- try
- {
- //第一個實參爲方法名,第二個實參爲方法參數類型對應的class對象
- Method nameMethod = clazz.getDeclaredMethod("setmName", String.class);
- Method ageMethod = clazz.getDeclaredMethod("setmAge", int.class);
- Method weightMethod = clazz.getDeclaredMethod("setmWeight", float.class);
- Method allValuesMethod = clazz.getDeclaredMethod("setAllValues", new Class[]{String.class, int.class, float.class});
- nameMethod.setAccessible(true);
- //調用setmName方法,給ReflectMethod類中的屬性mName賦值爲"stevenhu"
- nameMethod.invoke(clazz.newInstance(), "lisa");
- nameMethod = clazz.getDeclaredMethod("getmName", null);
- nameMethod.setAccessible(true);
- //調用getmName方法,得到mName的值
- String name1 = (String) nameMethod.invoke(clazz.newInstance(), null);
- System.out.println(name1);
- ageMethod.setAccessible(true);
- /*調用setmAge方法設置年齡,由於該方法是靜態方法,所以第一個實參可爲類的Class對象clazz,
- * 也可以是類的對象clazz.newInstance();
- */
- ageMethod.invoke(clazz, 21);
- ageMethod = clazz.getDeclaredMethod("getmAge", null);
- ageMethod.setAccessible(true);
- //調用getmAge方法,得到之前設置的年齡
- int age1 = (Integer) ageMethod.invoke(clazz, null);
- System.out.println(age1);
- weightMethod.setAccessible(true);
- //調用setmWeight方法,設置體重
- weightMethod.invoke(clazz.newInstance(), new Float(50.5));
- weightMethod = clazz.getDeclaredMethod("getmWeight",null);
- weightMethod.setAccessible(true);
- //調用getmWeight方法,得到之前設置的體齡
- float weight1 = (Float) weightMethod.invoke(clazz.newInstance(), null);
- System.out.println(weight1);
- allValuesMethod.setAccessible(true);
- /*調用ReflectMethod的setAllValues方法賦值
- * 注:此處不能直接傳入實參63.5;浮點型必須創建Float對象
- * 整型和字符串可創建Integer、String對象,也可以不創建
- */
- //allValuesMethod.invoke(clazz.newInstance(), new String("stevenhu"), new Integer(23), new Float(63.5));
- allValuesMethod.invoke(clazz.newInstance(), "stevenhu", 23, new Float(63.5));
- nameMethod = clazz.getDeclaredMethod("getmName", null);
- nameMethod.setAccessible(true);
- String name2 = (String) nameMethod.invoke(clazz.newInstance(), null);
- System.out.println(name2);
- ageMethod = clazz.getDeclaredMethod("getmAge", null);
- ageMethod.setAccessible(true);
- int age2 = (Integer) ageMethod.invoke(clazz.newInstance(), null);
- System.out.println(age2);
- weightMethod = clazz.getDeclaredMethod("getmWeight", null);
- weightMethod.setAccessible(true);
- float weight2 = (Float) weightMethod.invoke(clazz.newInstance(), null);
- System.out.println(weight2);
- //得到目標方法所在類對應的Class對象
- Class<?> clazz1 = weightMethod.getDeclaringClass();
- //得到目標方法拋出的異常類型對應的Class對象
- Class<?>[] clazzs1 = weightMethod.getExceptionTypes();
- for (Class cl : clazzs1)
- {
- System.out.println(cl);
- }
- //得到目標方法拋出的異常類型對應的Type對象
- Type[] types1 = weightMethod.getGenericExceptionTypes();
- //得到目標方法返回類型對應的Class對象
- Class<?> clazz2 = nameMethod.getReturnType();
- //得到目標方法返回類型對應的Type對象
- Type type = nameMethod.getGenericReturnType();
- //得到目標方法各參數類型對應的Class對象
- Class<?>[] clazzs2 = allValuesMethod.getParameterTypes();
- //得到目標方法各參數類型對應的Type對象
- Type[] types2 = allValuesMethod.getGenericParameterTypes();
- //得到目標方法修飾符的值
- int modifier = ageMethod.getModifiers();
- System.out.println(modifier);
- //得到目標方法的名字
- String methodName = nameMethod.getName();
- System.out.println(nameMethod.isVarArgs());
- } catch (NoSuchMethodException e)
- {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (SecurityException e)
- {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (IllegalAccessException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (IllegalArgumentException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (InvocationTargetException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (InstantiationException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
轉載:http://blog.csdn.net/stevenhu_223/article/details/9286121
轉載:http://blog.csdn.net/ritterliu/article/details/7764849