1. 程式人生 > >Java 反射呼叫類的屬性和方法(包含父類私有屬性和覆蓋重寫的方法等)

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();
    }
}

反射呼叫屬性的歸納總結

  1. 先判斷該屬性是否public修飾
  2. 若是,則使用getField()獲取。
  3. 反之,則判斷是否是類本身單獨定義屬性。
  4. 若是,則使用getDeclaredField()去獲取。
  5. 反之,則先通過getSuperclass()獲取到父類的Class物件,再去呼叫getDeclaredField()去獲取。

同理,反射呼叫方法和呼叫屬性的思路類似。