運行時類型信息可讓你在程序運行時發現和使用類型信息。java
在Java中運行時識別對象和類的信息有兩種方式:傳統的RTTI,以及反射。下面就來講說反射。數組
重點說說經過反射獲取方法以及調用方法,即類方法提取器。網絡
一、反射:spa
若是你不知道一個對象的肯定類型,RTTI能夠告訴你。可是有個限制:這個類型在編譯期間必須已知,才能使用RTTI來識別它。例如,在磁盤上或者網絡中的一段字符串,被告知表明一個類,可是編譯器在編譯代碼的時候,並不知道,怎麼才能使用這個類呢?這時候就須要使用反射。code
經過反射與未知類型的對象打交道時,JVM只是簡單地檢查這個對象,看它屬於哪一個特定的類(與RTTI同樣)。但在這以後,在作其它事情以前,必須加載那個類的 Class 對象。所以,那個類的.class 文件對於 JVM 來講必須是可獲取的,要麼在本地機器上,要麼能夠經過網絡取得。 對象
RTTI:編譯器在編譯期間打開和檢查.class文件blog
反射:編譯器在運行期間打開和檢查.class文件繼承
1.1類方法提取器字符串
1.1.1獲取方法get
步驟:
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調用方法
步驟:
先找到方法所在類的字節碼
找到須要被獲取的方法
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(null, new Object[]{17});//方法參數爲基本類型,且只有一個參數,解包後變成m.invoke(null,17}); m.invoke(null, new Object[]{「xxx」});//方法參數爲String類型,且只有一個參數 m.invoke(null, new Object[]{new int[]{1,2}});//方法參數爲int類型,且爲可變參數或者數組類型 m.invoke(null, new Object[]{new String[]{「A」,」B」}});//方法參數爲String類型,且爲可變參數或者數組類型,new String[]{「A」,」B」}爲傳遞的實際參數