1. 程式人生 > >Java反射機制,通過物件訪問呼叫類的私有成員屬性或者方法

Java反射機制,通過物件訪問呼叫類的私有成員屬性或者方法

Java反射機制原理
Java中萬物皆物件,類class也是物件,是一個名為Class的類的物件。
所以就可以通過這個Class類型別的物件class,用物件訪問類的屬性和方法。

Class是對類的抽象(每個類對應一份位元組碼)。一般情況下,比如A a = new A();
直接通過a物件呼叫方法即可。但是在有些場景下,這樣是做不到的,
比如類名A是通過引數傳遞過來的,這時候你就無法通過new的方法建立物件,需要先載入這個類,
獲取Method物件,然後用Method已反射的形式呼叫相應的方法。

如何獲得Class的類型別

先宣告一個類text的物件t;

text1 t 
= new text1();

有三種方式可以來獲得類型別:

Class t0 = text.class;//通過類獲得類型別,說明Class類中隱含著一個class的物件
Class t1 = t.getClass();//通過物件獲得類型別
Class t2 = null;
text t4 = null;
try {
    t2 = Class.forName("classReflex.text");// 通過類的全名獲得類型別
    t4 = (text) t0.newInstance();// 通過類型別獲得類的物件
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
    e.printStackTrace();
}

進入正題:通過類型別來實現對類的成員變數、方法、構造方法的訪問和呼叫。

首先是一個類text1

public class text1 {

    public int x;
    protected boolean b;
    private String s;

    public text1() {
        // TODO Auto-generated constructor stub
    }

    public void f0() {
        System.out.println("我愛計算機!我愛Android!");
    }

    private
void f0_private() { System.out.println("就算我是私有的方法!我愛計算機!我愛Android!"); } public void f1(int i, String s1, boolean b) { } public int f2(int i, int s1, int b) { return i; } public String f3(String i, String s1, String b) { return i; } }

利用物件獲得類的成員變數

  1. 利用物件獲得類型別Class c = object.getClass();
  2. 通過getName()方法獲得類的名稱。
  3. getDeclaredFields()會返回類自己定義的所有的成員屬性的Field集合,(領域)
  4. 通過Class type = field.getType();獲得屬性的類型別,getName()就能得到屬性的具體型別了
  5. 關於屬性的呼叫類似於下文的方法的呼叫
public static void printClassAttribute(Object object) {
        // 首先獲得類的資訊,先獲得類的型別。
        Class c = object.getClass();
        // 獲得類的名字,傳入的是什麼型別,就表示什麼型別,而不是父類object型別。
        System.out.println("類的名稱:" + c.getName());
        System.out.println("類的成員屬性:");
        Field[] Fields = c.getDeclaredFields();
        if (Fields.length > 0) {
            for (Field field : Fields) {
                Class type = field.getType();
                System.out.println("field.getName()-->" + field.getName());
                System.out
                        .println(field.getModifiers() + " " + type.getSimpleName() + 
                        " m" + type.getSimpleName() + ";");
            }
        } else {
            System.out.println("沒有成員屬性");
        }
    }

利用物件獲得類的成員方法

通過類型別物件訪問類所有的方法

  1. c.getDeclaredMethods();通過類型別的物件獲得方法類的物件集合Method的集合。
  2. 通過Method物件的getReturnType()獲得方法的返回型別的類型別物件
  3. 同股票method.getParameterTypes()獲得方法的輸入形參的類型別集合。之後就好辦啦,通過類型別得到型別。

通過類型別物件呼叫類的方法

  1. Methodm m= c.getMethod(“方法名”, 形參的類型別集合);獲得指定方法的物件
  2. 如果該方法是私有方法需要現宣告該方法訪問無障礙setAccessible(true);
  3. 之後呼叫m.invoke(c.newInstance,”實參”集合);呼叫類的方法。
  4. 當然如果是靜態方法就不需要傳入型別物件,只需要傳入類型別物件c就可以了
public static void printClassMethod(Object object) {
        // 首先獲得類的資訊,先獲得類的型別。
        Class c = object.getClass();
        // 獲得類的名字,傳入的是什麼型別,就表示什麼型別,而不是父類object型別。
        System.out.println("類的名稱:" + c.getName());
        System.out.println("類的成員方法:");
        /*
         * Method型別是方法的型別。即:方法也是物件,是型別Method的物件
         * getMethods()獲得類的所有成員方法,包括從父類繼承來的方法
         * getDeclaredMethods()獲得類自己定義的成員方法。不包含父類的方法
         */
        // Method[] methods = c.getMethods();
        Method[] methods = c.getDeclaredMethods();
        for (Method method : methods) {
            // 獲得方法的返回值的類型別
            Class returnTyoe = method.getReturnType();
            System.out.print(returnTyoe.getSimpleName() + " " + method.getName() + "(");
            // 獲得方法所有的形引數組
            Class[] parameterTypes = method.getParameterTypes();
            for (int i = 0; i < parameterTypes.length; i++) {
                Class parameterType = parameterTypes[i];
                if (i != 0)
                    System.out.print(" , ");
                System.out.print(parameterType.getSimpleName() + " par" + i);
            }
            System.out.println(");");
        }
        System.out.println("methods.length-->" + methods.length);
        /**
         * 利用反射呼叫public方法f0();<br/>
         * 先獲得方法的物件Method型別的,由方法物件呼叫物件的方法
         */
        Method m;
        try {
            m = c.getMethod("f0", null);
            m.invoke(c.newInstance(), null);// 通過類的物件呼叫物件的方法,後面的是引數
            // 如果的呼叫靜態方法,就不必傳入類的物件了,而只需要傳遞類型別就可以了m.invoke(c, null);
        } catch (NoSuchMethodException | SecurityException | IllegalAccessException | 
        IllegalArgumentException
                | InvocationTargetException | InstantiationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        /**
         * 利用反射呼叫private方法f0(); <br/>
         * m_private.setAccessible(true);//訪問私有方法的關鍵步驟,設定為訪問無障礙
         */
        Method m_private;
        try {
            m_private = c.getDeclaredMethod("f0_private", null);
            m_private.setAccessible(true);// 訪問私有方法的關鍵步驟,設定為訪問無障礙,但是也只能訪問該類型別能訪問到的方法,子類就無法訪問父類的私用方法
            m_private.invoke(c.newInstance(), null);
        } catch (NoSuchMethodException | SecurityException | IllegalAccessException | 
        IllegalArgumentException| InvocationTargetException | InstantiationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

利用物件獲得類的構造方法

和呼叫成員方法類似,不再做過多講解。

public static void printStructureMethod(Object object) {
        Class c = object.getClass();
        // 獲得類的名字,傳入的是什麼型別,就表示什麼型別,而不是父類object型別。
        System.out.println("類的名稱:" + c.getName());
        System.out.println("類的構造方法:");
        Constructor[] constructors = c.getConstructors();
        for (int i = 0; i < constructors.length; i++) {
            Constructor constructor = constructors[i];
            System.out.print(constructor.getName() + " (");
            Class[] parameterTypes = constructor.getParameterTypes();
            for (int j = 0; j < parameterTypes.length; j++) {
                Class parameterType = parameterTypes[j];
                if (j != 0)
                    System.out.print(" , ");
                System.out.print(parameterType.getSimpleName());
            }
            System.out.println(")");
        }
    }