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代表的型別物件是否能給指定型別進行賦值,原理是父類物件可以讓子類引用。