類型信息(RTTI和反射)——反射

運行時類型信息可讓你在程序運行時發現和使用類型信息。java

在Java中運行時識別對象和類的信息有兩種方式:傳統的RTTI,以及反射。下面就來講說反射。數組

重點說說經過反射獲取方法以及調用方法,即類方法提取器網絡

一、反射:spa

  若是你不知道一個對象的肯定類型,RTTI能夠告訴你。可是有個限制:這個類型在編譯期間必須已知,才能使用RTTI來識別它。例如,在磁盤上或者網絡中的一段字符串,被告知表明一個類,可是編譯器在編譯代碼的時候,並不知道,怎麼才能使用這個類呢?這時候就須要使用反射。code

  經過反射與未知類型的對象打交道時,JVM只是簡單地檢查這個對象,看它屬於哪一個特定的類(與RTTI同樣)。但在這以後,在作其它事情以前,必須加載那個類的 Class 對象。所以,那個類的.class 文件對於 JVM 來講必須是可獲取的,要麼在本地機器上,要麼能夠經過網絡取得。 對象

       RTTI:編譯器在編譯期間打開和檢查.class文件blog

       反射:編譯器在運行期間打開和檢查.class文件繼承

1.1類方法提取器字符串

 1.1.1獲取方法get

步驟:

  1. 先找到方法所在類的字節碼 
  2. 找到須要被獲取的方法

Class類中獲取方法:

public Method[] getMethods();//獲取包括自身和繼承(實現)過來的全部的public方法——Method不支持泛型<>,即後面不接<>

public Method[] getDeclaredMethods();//獲取自身全部的方法(private、public、protected,和訪問權限無關),不包括繼承的

public Method[] getMethod(String methodName, Class<T>...parameterTypes);//表示獲取指定的一個公共的方法,包括繼承的

public Method[] getDeclaredMethod(String methodName, Class<T>...parameterTypes);//表示獲取本類中的一個指定的方法(private、protected、public,與訪問權限無關),不包括繼承的方法

其中參數: methodName:表示獲取的方法的名字

                   parameterTypes:表示獲取的方法的參數的Class類型

1.1.2調用方法

 步驟:

  1. 先找到方法所在類的字節碼

  2. 找到須要被獲取的方法

  3. 調用該方法
class User{

  public void sayHello(){...}

  public void sayHi(String naem){...}

  private  void sayGoodBye(String name, int age){...}

}

如何使用反射調用一個方法?

在Method類中有一個方法:

 

public Object invoke(Object  obj,  Object...  args);//表示調用當前Method所表示的方法

參數:  obj: 表示被調用方法底層所屬對象

             args: 表示調用方法時傳遞的實際參數

     返回:方法調用後,底層方法的返回結果

Class<User> clz=User.class;

Method mt=clz.getMethod(「sayHi」, String.class);

Object obj=clz.newInstance();

Object ret=mt.invoke(obj, 「wili」);//要調用實例方法,必須有一個對象,方法的底層對象就是指當前Method所在的類的實例對象,sayHi方法具備返回值,調用該方法後的返回結果使用Object接收

如何調用私有方法?

Method mt=clz.getDeclaredMethod(「sayGoodBye」, String.class, int.class);

//在調用私有方法以前,需設置該方法爲可訪問的權限:——不然會報錯

mt.setAccessible(true);

mt.invoke(clz.newInstance(), 「limi」, 17);

如何調用靜態方法?

class User{

    public static void staticMethod(){

       System.out.println(「static mthod invoke.」);

    }

}
Class<User> clz=User.class;

Method staticMethod=clz.getMethod(「staticMthod」);

//兩種方式調用靜態方法:

//1. 由於靜態方法屬於全部實例對象公共的,能夠建立該類的一個任意對象,經過該對象調用

 staticMethod.invoke(clz.newInstance());//staticMethod無參,故參數列表類型不填

//2. 若是底層方法是靜態的,那麼能夠忽略指定的obj參數,將obj參數設置爲null便可

staticMethod.invoke(null);

如何調用可變參數的方法?

class User{

    public static int sum(int[] ages){
System.out.println(args);
//打印結果可看出:可變參數底層就是一個數組 int sum=0;
  
for(int i : args){ sum+=i; } return sum; } public static void show(String[] args){...} }
Class<User> clz=User.class;

Method m=clz.getMethod(「sum」, int[].class);//可變參數底層就是一個數組

M.invoke(null,  new int[]{1,2,3});

Method m=clz.getMethod(「show」, String[].class);

//M.invoke(null,  new String[]{「A」,」B」,」C」});//會報錯,可變參數是引用類型時,底層會自動解包,上述調用被解包後變成M.invoke(null,「A」,」B」,」C」);——爲了解決該問題,咱們再使用一層數組把實際參數包裝起來

M.invoke(null,  new Object[]{new String[]{「A」,」B」,」C」}});//正確

通用方法:

之後在使用反射調用invoke方法時,在傳遞實際參數的時候,不管是基本數據類型,仍是引用類型,或者是可變參數類型,把實際參數都包裝在一維數組中。

m.invoke(方法的底層對象,new Object[]{實際參數});

m.invoke(nullnew Object[]{17});//方法參數爲基本類型,且只有一個參數,解包後變成m.invoke(null,17});

m.invoke(nullnew Object[]{「xxx」});//方法參數爲String類型,且只有一個參數

m.invoke(nullnew Object[]{new int[]{1,2}});//方法參數爲int類型,且爲可變參數或者數組類型

m.invoke(nullnew Object[]{new String[]{「A」,」B」}});//方法參數爲String類型,且爲可變參數或者數組類型,new String[]{「A」,」B」}爲傳遞的實際參數
相關文章
相關標籤/搜索