關於Java反射,文章Java反射之基礎篇已基本介紹了反射的用法,但是反射的整個調用過程仍比較繁瑣,尤其是對於新手,顯得比較晦澀。下面介紹些更為簡單有效的反射實用內容。
一、反射用法#
前面介紹到,反射是為了在運行態能操作類和對象,接下來重點介紹如何反射使用。
對於正常方式來調用方法,往往只需要一行到兩行代碼,即可完成相應工作。而反射則顯得比較繁瑣,之所以繁瑣仍會才用反射方式,是因為反射能干很多正常實例化對象的方式所無法做到的事。比如操作那些private的類、方法、屬性,以及@hide標記過的類、方法、屬性。
為了到達即能有反射的功效,同時調用方法簡單易用,寫了一個ReflectUtils類。對於方法調用,與正常對象的調用過程差不多。主要由以下4類需要用到反射的地方:
1.調用類的靜態方法
2.調用類的非靜態方法
3.set/get類的靜態屬性
4.set/get類的非靜態屬性
1.1 ReflectUtils類用法#
調用流程一般為先獲取類或對象,再調用相應方法。針對上述4種需求,用法分別如下:
1. 調用類的靜態方法
對於參數方法,只需把參數,緊跟方法名後面,可以跟不定長的參數個數。
Class<?> clazz = ReflectUtils.getClazz("com.yuanhh.model.Outer"); //獲取class
ReflectUtils.invokeStaticMethod(clazz, "outerStaticMethod"); //無參方法
ReflectUtils.invokeStaticMethod(clazz, "outerStaticMethod","yuanhh"); //有參數方法
2. 調用類的非靜態方法
Object obj = ReflectUtils.newInstance("com.yuanhh.model.Outer"); //實例化對象
ReflectUtils.invokeMethod(obj, "outerMethod"); //無參方法
ReflectUtils.invokeMethod(obj, "outerMethod", "yuanhh"); //有參方法 **3. set/get類的靜態屬性**
ReflectUtils.getStaticField(clazz, "outerStaticField"); //get操作
ReflectUtils.setStaticField(clazz, "outerStaticField", "new value"); //set操作
4. set/get類的非靜態屬性
ReflectUtils.getField(obj, "outerField"); //get操作
ReflectUtils.setField(obj, "outerField", "new value"); //set操作
如果只知道類名,需先查看該類的所有方法詳細參數信息,可以通過調用dumpClass(String className) ,返回值是String,記錄著所有構造函數,成員方法,屬性值的信息。
1.2 核心代碼#
關於ReflectUtils類,列表部分核心方法。
先定義一個Outer類, 包名假設為com.yuanhh.model,對於該類,非public,構造方法,成員方法,屬性都是private:
class Outer {
private String outerField = "outer Field";
private static String outerStaticField = "outer static Field";
private Outer(){
System.out.println("I'am outer construction without args");
}
private Outer(String outerField){
this.outerField = outerField;
System.out.println("I'am outer construction with args "+ this.outerField);
}
private void outerMethod(){
System.out.println("I'am outer method");
}
private void outerMethod(String param){
System.out.println("I'am outer method with param "+param);
}
private static void outerStaticMethod(){
System.out.println("I'am outer static method");
}
private static void outerStaticMethod(String param){
System.out.println("I'am outer static method with param "+param);
}
}
構造函數,獲取類的實例化對象:
/**
* 實例化獲取類名對應的類
*
* @param clazz 類
* @param constructorArgs 構造函數的各個參數
* @return 實例化對象
*/
public static Object newInstance(Class clazz, Object... constructorArgs) {
if (clazz == null) {
return null;
}
Object object = null;
int argLen = constructorArgs == null ? 0 : constructorArgs.length;
Class<?>[] parameterTypes = new Class[argLen];
for (int i = 0; i < argLen; i++) {
parameterTypes[i] = constructorArgs[i].getClass();
}
try {
Constructor constructor = clazz.getDeclaredConstructor(parameterTypes);
if (!constructor.isAccessible()) {
constructor.setAccessible(true);
}
object = constructor.newInstance(constructorArgs);
} catch (Exception e) {
e.printStackTrace();
}
return object;
}
對象方法的反射調用如下:
/**
* 反射調用方法
*
* @param object 反射調用的對象實例
* @param methodName 反射調用的對象方法名
* @param methodArgs 反射調用的對象方法的參數列表
* @return 反射調用執行的結果
*/
public static Object invokeMethod(Object object, String methodName,
Object... methodArgs) {
if (object == null) {
return null;
}
Object result = null;
Class<?> clazz = object.getClass();
try {
Method method = clazz.getDeclaredMethod(methodName, obj2class(methodArgs));
if (method != null) {
if (!method.isAccessible()) {
method.setAccessible(true); //當私有方法時,設置可訪問
}
result = method.invoke(object, methodArgs);
}
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
對象屬性值的反射獲取方法:
/**
* 反射調用,獲取屬性值
*
* @param object 操作對象
* @param fieldName 對象屬性
* @return 屬性值
*/
public static Object getField(Object object, String fieldName) {
if (object == null) {
return null;
}
Object result = null;
Class<?> clazz = object.getClass();
try {
Field field = clazz.getDeclaredField(fieldName);
if (field != null) {
if (!field.isAccessible()) {
field.setAccessible(true);
}
result = field.get(object);
}
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
類屬性的反射設置過程:
/**
* 反射調用,設置屬性值
*
* @param clazz 操作類
* @param fieldName 屬性名
* @param value 屬性的新值
* @return 設置是否成功
*/
public static boolean setStaticField(Class clazz, String fieldName, Object value) {
if (clazz == null) {
return false;
}
Object result = null;
try {
Field field = clazz.getDeclaredField(fieldName);
if (field != null) {
if (!field.isAccessible()) {
field.setAccessible(true);
}
field.set(null, value);
}
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
二、內部類的反射用法#
對於內部類,這裡比較復雜,而內部類又分static內部類與非static內部類,兩者的反射方式還是有區別的,剛開始在這邊折騰了好一陣子,一直反射失敗。static內部類與非static內部類的反射調用,根本區別在於構造方法不一樣。下面通過代碼來告訴如何正確。
2.1 static與非static內部類的反射差異#
先定義一個包含兩個內部類的類:
class Outer {
/**
* 普通內部類
*/
class Inner {
private String innerField = "inner Field";
private Inner(){
System.out.println("I'am Inner construction without args");
}
private Inner(String innerField){
this.innerField = innerField;
System.out.println("I'am Inner construction with args "+ this.innerField);
}
private void innerMethod(){
System.out.println("I'am inner method");
}
}
/**
* 靜態內部類
*/
static class StaticInner {
private String innerField = "StaticInner Field";
private static String innerStaticField = "StaticInner static Field";
private StaticInner(){
System.out.println("I'am StaticInner construction without args");
}
private StaticInner(String innerField){
this.innerField = innerField;
System.out.println("I'am StaticInner construction with args "+ this.innerField);
}
private void innerMethod(){
System.out.println("I'am StaticInner method");
}
private static void innerStaticMethod(){
System.out.println("I'am StaticInner static method");
}
}
}
對於上面兩個內部類,如果直接實例化內部類,該怎麼做,拋開private等權限不夠的問題,應該是這樣的:
•靜態內部類:Outer.StaticInner sInner = new Outer.StaticInner();
•非靜態內部類: Outer.Inner inner = new Outer().new Inner();
這種差異,在於內部類的構造方法不一樣。我們可以通過下面的方法dumpClass()來比較。
/**
* 獲取類的所有 構造函數,屬性,方法
*
* @param className 類名
* @return
*/
public static String dumpClass(String className) {
StringBuffer sb = new StringBuffer();
Class<?> clazz;
try {
clazz = Class.forName(className);
} catch (ClassNotFoundException e) {
e.printStackTrace();
return "";
}
Constructor<?>[] cs = clazz.getDeclaredConstructors();
sb.append("------ Constructor ------> ").append("\n");
for (Constructor<?> c : cs) {
sb.append(c.toString()).append("\n");
}
sb.append("------ Field ------>").append("\n");
Field[] fs = clazz.getDeclaredFields();
for (Field f : fs) {
sb.append(f.toString()).append("\n");
;
}
sb.append("------ Method ------>").append("\n");
Method[] ms = clazz.getDeclaredMethods();
for (Method m : ms) {
sb.append(m.toString()).append("\n");
}
return sb.toString();
}
通過dumpClass(),對比我們會發現,
•static內部類的默認構造函數: private void com.yuanhh.model.Outer$StaticInner.innerMethod()
•非static內部類的默認構造函數: private com.yuanhh.model.Outer$Inner(com.yuanhh.model.Outer),多了一個參數com.yuanhh.model.Outer,也就是說非static內部類保持了外部類的引用。從屬性,我們也會發現多了一個final屬性final com.yuanhh.model.Outer com.yuanhh.model.Outer$Inner.this$0,這正是用於存儲外部類的屬性值。
正是這差異,導致兩者的反射調用過程中構造方法的使用不一樣。另外內部類的類名使用采用$符號,來連接外部類與內部類,格式為outer$inner。
2.2 static內部類的 ReflectUtils類用法#
1.調用類的靜態方法(先獲取類,再調用)
Class<?> clazz = ReflectUtils.getClazz("com.yuanhh.model.Outer$StaticInner"); //獲取class
ReflectUtils.invokeStaticMethod(clazz, "innerStaticMethod"); //無參方法
ReflectUtils.invokeStaticMethod(clazz, "innerStaticMethod","yuanhh"); //有參數方法
2.調用類的非靜態方法(先獲取對象,再調用)
Object obj = ReflectUtils.newInstance("com.yuanhh.model.Outer$StaticInner"); //實例化對象
ReflectUtils.invokeMethod(obj, "innerMethod"); //無參方法
ReflectUtils.invokeMethod(obj, "innerMethod", "yuanhh"); //有參方法
3.set/get類的靜態屬性(先獲取類,再調用)
ReflectUtils.getStaticField(clazz, "innerField"); //get操作
ReflectUtils.setStaticField(clazz, "innerField", "new value"); //set操作
4.set/get類的非靜態屬性(先獲取對象,再調用)
ReflectUtils.getField(obj, "innerField"); //get操作
ReflectUtils.setField(obj, "innerField", "new value"); //set操作 ### 2.2 非static內部類的 `ReflectUtils`類用法 非static內部類,不能定義靜態方法和靜態屬性,故操作只有兩項:
5.調用類的非靜態方法(先獲取對象,再調用)
// 獲取外部類實例,這是static內部類所不需要的,注意點
Object outObj = ReflectUtils.newInstance("com.yuanhh.model.Outer");
Object obj = ReflectUtils.newInstance("com.yuanhh.model.Outer$Inner", outObj); //實例化對象
ReflectUtils.invokeMethod(obj, "innerMethod"); //無參方法
ReflectUtils.invokeMethod(obj, "innerMethod", "yuanhh"); //有參方法
6.set/get類的非靜態屬性(先獲取對象,再調用)
ReflectUtils.getField(obj, "innerField"); //get操作
ReflectUtils.setField(obj, "innerField", "new value"); //set操作
三、小結#
•主要ReflectUtils類的用法,只需要按1.1 ReflectUtils類用法的方式使用即可,比如反射調用方法,只需知道類與方法名,即可調用完成invokeMethod(Object object, String methodName)操作簡單。
•static與非static內部類的區別,在2.2與2.3這兩節,對於內部類差異僅僅在於傳遞參數多了一個$符號,以及非static內部類實例化需要加上外部類的實例。
•ReflectUtils類,以及本文所有涉及的代碼,即將打包上傳。