Java 反射呼叫類的屬性和方法(包含父類私有屬性和覆蓋重寫的方法等)
前面介紹了,反射呼叫類的構造方法來建立類的例項物件。一個類的結構包含方法(構造,靜態,非靜態)和屬性(靜態和非靜態)。按照迴圈漸進的方式,接下來,介紹反射類中屬性和普通的方法。
在這裡簡單介紹,反射呼叫屬性和方法會用到的新類,Method類和Field類。
Method類的常用API:
getModifiers()
: 獲取方法的修飾符getName()
: 獲取到方法的名稱getParameterTypes()
: 獲取到方法的全部引數型別getReturnType()
: 獲取到方法的返回值型別getException()
:獲取方法中全部丟擲的異常invoke(Object obj,Object ... args)
Field類的常用API:
get(Object obj)
:獲取到屬性的具體內容set(Object ,Object value)
:設定指定屬性的具體內容getModifiers()
: 獲取屬性的修飾符isAccessible()
:判斷屬性是否可以被外部訪問setAccessible(boolean flag)
:設定這屬性可以被外部訪問。
案例實戰
1. 定義一個介面:
定義一些行為,作為抽象方法。用於測試,反射呼叫實現類(覆蓋重寫的)方法。
package com.xingen.classdemo;
public interface ClassTestInterface {
void testMethod(String name,String work);
}
2. 構建一個父類:
新增一些屬性,進行封裝。用於測試,反射呼叫父類私有屬性。
package com.xingen.classdemo;
import java.lang.reflect.Constructor;
public class ClassTest2 {
private String name;
private String work;
/**
* 構建一個預設的構造方法
*/
public ClassTest2() {
}
/**
* 構建有引數構造方法
*
* @param name
* @param work
*/
public ClassTest2(String name, String work) {
this.name = name;
this.work = work;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getWork() {
return work;
}
public void setWork(String work) {
this.work = work;
}
}
3. 建立一個子類:
該子類的作用有以下幾點:
繼承父類
實現若干介面,覆蓋重寫抽象方法,例如:
testMethod(String name, String work)
定義自己本身的私有屬性,例如:
age
欄位
package com.xingen.classdemo;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class ClassTest3 extends ClassTest2 implements ClassTestInterface {
private int age;
public ClassTest3() {
}
@Override
public void testMethod(String name, String work) {
this.setName(name);
this.setWork(work);
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
StringBuffer stringBuffer = new StringBuffer();
if (getName() != null) {
stringBuffer.append("name: ");
stringBuffer.append(getName());
stringBuffer.append("\n");
}
if (getWork() != null) {
stringBuffer.append("work: ");
stringBuffer.append(getWork());
stringBuffer.append("\n");
}
if (getAge() > 0) {
stringBuffer.append("age: ");
stringBuffer.append(getAge());
}
return stringBuffer.toString();
}
}
4. 各種常用的反射場景:
4.1 案例之反射獲取父類的資訊:
先獲取到Class物件,然後呼叫getSuperclass()
獲取到父類的Class物件。
眾所眾知,單繼承,多實現,因此父類只有一個。這裡,輸出父類所屬於的包資訊。
/**
* 獲取父類的資訊
*/
public static void testSuperClass() {
try {
Class<?> mClass = ClassTest3.class;
//獲取繼承的父類
Class<?> superClass = mClass.getSuperclass();
System.out.println("獲取繼承父類的包路徑:\n" + superClass.getName());
} catch (Exception e) {
e.printStackTrace();
}
}
輸出結果是:
獲取繼承父類的包路徑:
com.xingen.classdemo.ClassTest2
4.2 案例之反射獲取實現介面的資訊:
先獲取到Class物件,然後呼叫getInterfaces()
獲取到全部實現介面的Class陣列。因ClassTest3實現了一個介面,所以這裡的陣列索引值是0。介面的Class物件可以獲取到介面中的完整資訊,這裡輸出介面所屬於的包資訊。
/**
* 獲取實現介面的資訊
*/
public static void testInterface() {
try {
Class<?> mClass = ClassTest3.class;
/**
* 獲得全部實現的介面:Class<?>[] getInterfaces()得到的陣列中,介面物件順序和這個物件所表示的類中implements子句中介面名的順序,是一致的。
*/
Class<?>[] interfaceArray = mClass.getInterfaces();
Class<?> interfaces = interfaceArray[0];
System.out.println("獲取實現介面的包路徑: \n" + interfaces.getName());
} catch (Exception e) {
e.printStackTrace();
}
}
輸出結果是:
獲取實現介面的包路徑:
com.xingen.classdemo.ClassTestInterface
4.3 案例之反射呼叫覆蓋重寫的方法
先獲取Class物件,然後通過getMethod()
獲取到指定的需要呼叫的方法。接下來,處理是否需要新增訪問許可權setAccessible(true)
,最後通過invoke()
進行方法呼叫。
/**
* 測試覆蓋重寫的方法
*/
public static void testSuperMethod() {
try {
Class<ClassTest3> mClass = ClassTest3.class;
ClassTest3 instance = mClass.newInstance();
Method method = mClass.getMethod("testMethod", String.class, String.class);
if (!method.isAccessible()) {
method.setAccessible(true);
}
method.invoke(instance, "xinGen", "Android Lirary Developer");
System.out.println("反射訪問覆蓋重寫的方法:\n " + instance.toString());
} catch (Exception e) {
e.printStackTrace();
}
}
輸出結果是:
反射訪問覆蓋重寫的方法:
name: xinGen
work: Android Lirary Developer
4.4 案例之反射呼叫本類定義的私有屬性
先獲取到Class物件,然後通過getDeclaredField()
獲取到本類定義的私有屬性,再呼叫setAccessible()
賦予訪問許可權,最後呼叫set()
對私有屬性內容修改。
/**
* 測試呼叫本身定義的私有屬性
*/
public static void testSelfField() {
try {
Class<ClassTest3> mClass = ClassTest3.class;
ClassTest3 instance = mClass.newInstance();
Field field = mClass.getDeclaredField("age");
field.setAccessible(true);
field.set(instance, 24);
System.out.println("反射訪問本類中Private修飾的屬性:\n " + instance.toString());
} catch (Exception e) {
e.printStackTrace();
}
}
輸出結果是:
反射訪問本類中Private修飾的屬性:
age: 24
4.5 案例之反射呼叫父類中私有屬性
先獲取到Class物件,然後通過getSuperclass()
獲取到父類的Class物件,再呼叫getDeclaredField()
獲取到父類的私有屬性,最後呼叫set()
,對私有屬性進行內容修改。這裡,最後輸出修改後的內容。
/**
* 測試呼叫父類私有屬性
*/
public static void testSuperField() {
try {
Class<ClassTest3> mClass = ClassTest3.class;
ClassTest3 instance = mClass.newInstance();
Field field = mClass.getSuperclass().getDeclaredField("name");
field.setAccessible(true);
field.set(instance, "xinGen");
System.out.println("反射訪問父類中Private修飾的屬性:\n " + instance.toString());
} catch (Exception e) {
e.printStackTrace();
}
}
輸出結果是:
反射訪問父類中Private修飾的屬性:
name: xinGen
5. 執行main()
入口程式
package com.xingen.classdemo;
public class Client {
public static void main(String[] args) {
useClassFieldAndMethod();
}
/**
* 使用Class類:讀寫屬性和方法
*/
public static void useClassFieldAndMethod() {
ClassTest3.testInterface();
ClassTest3.testSuperClass();
ClassTest3.testSuperMethod();
ClassTest3.testSelfField();
ClassTest3.testSuperField();
}
}
反射呼叫屬性的歸納總結:
- 先判斷該屬性是否public修飾
- 若是,則使用getField()獲取。
- 反之,則判斷是否是類本身單獨定義屬性。
- 若是,則使用getDeclaredField()去獲取。
- 反之,則先通過getSuperclass()獲取到父類的Class物件,再去呼叫getDeclaredField()去獲取。
同理,反射呼叫方法和呼叫屬性的思路類似。