1. 程式人生 > >Java反射框架(二)——Field、Method

Java反射框架(二)——Field、Method

目錄

3.Members

3.1Fields

域是關聯值的類、介面、列舉。

java.lang.reflect.Field中的方法可以檢索域的資訊,eg:名稱、型別、修飾符、註解。有些方法還支援動態訪問修改域值。

獲取域名

String s = f.getName();

獲取指定域

Field f = c.getField(fn);
Field f = c.getDeclaredField(fn);

獲取所有域

Field[] flds = = c.getDeclaredFields();
Field[] fields = c.getFields();

獲取域型別

Class c = f.getType();

note:返回表示Field物件f表示的域的型別的Class物件。

ps:如果型別為泛型,會受到型別擦除的影響。

Type t = f.getGenericType();

note:返回表示Field物件f表示的域的類性的Type物件。

ps:會從class檔案的簽名屬性中讀取型別資訊,所以不會受到泛型的型別擦除機制的影響。

檢索解析域修飾符

獲取修飾符

int foundMods = f.getModifiers();

note:獲取域f的修飾符集合。

ps:修飾符在java中的表現形式是Modifier中定義的靜態常量,修飾符集合就是這些十六位int常量|操作之後的結果。

ps:可以使用Modifier解析這個int,獲取域的所有修飾符資訊。

判斷域是否為合成域

boolean b = f.isSynthetic();

ps:合成域的意思是java編譯器自動引入的域。

ps:內部類會自動引入合成域this$0,用於指向外部類。

ps:列舉類會自動引入合成域ENUM$VALUES。

ps:合成域由編譯器決定,所以不同編譯器可能會產生名稱不同的合成域。

ps:合成域可以通過getDeclaredFields()獲取。

判斷域是否為列舉型別元素

boolean b = f.isEnumConstant();

ps:列舉類中的域皆是列舉型別元素。

判斷域的修飾符是否包含給定修飾符

int searchMods = 0x0;
searchMods |= Modifier.PUBLIC;
int foundMods = f.getModifiers();
if((foundMods & searchMods) == searchMods) {
    ...				
}

note:如果域f為public,則執行if語句塊。

Field實現了AnnotatedElement介面,所以可以獲取持有的註解。

獲取設定域值

獲取原始型別域的值

long l = f.getLong(obj);

note:獲取物件obj的long型別f域值l。

ps:getInt、getDouble、getFloat、getShort、getLong、getByte、getChar、getBoolean。

獲取引用型別域的值

Object o = f.get(ft);

設定原始型別域的值

f.setLong(ftt, 33333);

note:將ftt物件的f域設定為33333,f域的型別為long。

ps:setInt、setDouble、setFloat、setShort、setLong、setByte、setChar、setBoolean。

設定引用型別域的值

f.set(ft, sss);

note:將ft物件的f域設定為物件sss,f域的型別為引用型別。

不能使用反射來修改final常量。

3.2Methods

反射程式碼將方法選擇限定在一個特定類之中,而不考慮它的父類。

獲取方法名

String s = m.getName();

獲取指定方法

m = c.getMethod("run", int.class);
m = c.getDeclaredMethod("run", int.class);

ps:如果獲取的方法的引數使用了泛型,使用java.lang.Object。

獲取所有方法

Method[] allMethods = c.getMethods();
Method[] allMethods = c.getDeclaredMethods();

獲取方法型別資訊

獲取方法名

String ss = m.toGenericString();

獲取方法返回值型別

Class c = m.getReturnType();
Type t = m.getGenericReturnType();

ps:前者在遭遇泛型時,返回Object的Class,後者返回表示泛型引數的Type。

獲取方法引數型別列表

Class<?>[] mty = m.getParameterTypes();
Type[] gty = m.getGenericParameterTypes();

ps:前者在遭遇泛型時,返回Object的Class,後者返回表示泛型引數的Type。

ps:如果獲取的引數為可變引數,此引數返回一個元件型別為引數型別的陣列的Class。

獲取丟擲異常列表

Class<?>[] ety = m.getExceptionTypes();
Type[] gety = m.getGenericExceptionTypes();

獲取方法引數名

位元組碼檔案預設不儲存引數名稱。

使用javac編譯原始碼時,使用-parameters選項可以將引數名稱存入.class檔案中,供反射機制獲取。

使用java.lang.reflect.Parameter作為介面操作引數。

獲取引數列表

Parameter[] ps = m.getParameters();

獲取引數名稱

String s = p.getName();

ps:如果沒有設定javac的-parameters選項,根據引數列表順序返回arg0、args1……

獲取引數型別

Class c = p.getType();

獲取引數修飾符

int m = p.getModifiers();

ps:可以使用Modifier.toString(),將得到的int表示的引數修飾符集合轉變為可讀字串。

判斷引數是否有名稱

boolean b = p.isNamePresent();

判斷引數是否為隱式引數

boolean b = p.isImplicit();

ps:java編譯器會為內部類建立一個包含外部類引用引數的預設構造方法,此引數為隱式引數,此隱式引數為final與implicit。

判斷引數是否為合成引數

boolean b = p.isSynthetic();

ps:編譯器引入的沒有在原始碼中顯式或隱式宣告的結構,除了類初始化方法,都屬於合成。

ps:Enum結構,預設由編譯器建立構造器、values()、valueOf(String name)。

ps:合成方法內的引數皆是合成引數。

檢索分析方法修飾符

獲取方法修飾符

int mod = m.getModifiers();

判斷方法是否含有變參

boolean b = m.isVarArgs();

note:包含返回true。

判斷方法是否為橋接方法

boolean b = m.isBridge();

note:橋接方法返回true。

ps:橋接方法是,編譯器在是實現泛型介面的類中,建立的以Object作為引數型別,並且呼叫實現類中的方法的中介方法。

ps:橋接方法就是一種合成方法。

判斷方法是否為合成方法

boolean b = m.isSynthetic();

Method實現了AnnotatedElement介面,可以以此獲取Method持有的註解。

呼叫方法

反射提供了呼叫類方法的API。

通常情況下,當不能類例項轉化為需要的型別時,反射是呼叫方法的唯一途徑。

反射呼叫方法

m.invoke(obj, null);

note:呼叫物件的Method物件m代表的無參方法。

ps:呼叫靜態方法時,第一個引數傳null。

ps:呼叫無參函式時,第一個引數之後的引數可以不傳。

ps:方法的引數數量不定時,獲取方法時使用陣列型別的Class鎖定方法,呼叫時傳入陣列。

ps:呼叫的方法如果為private,將會丟擲異常。

ps:使用AccessibleObject.setAccessible()設定true,強制訪問private。

獲取反射呼叫方法丟擲的異常

Throwable t = e.getCause();

ps:反射呼叫的方法丟擲的異常會被反射框架的InvocationTargetException包裝,通過getCause()方法可以獲取原異常。

檢驗當前class與指定class是否相同,或是其父類

boolean b = YU.class.isAssignableFrom(TY.class);

note:檢測YU是否與TY相同,或者YU為TY的父類。如果是,返回true。

ps:判斷呼叫方法的Class代表的型別物件是否能給指定型別進行賦值,原理是父類物件可以讓子類引用。