java.lang.reflect包的淺析

Object(源頭)

Java的一切都是對象java

Object是反射的源頭數組

Class是反射的導演函數

Class(導演)

在Java程序運行時,JVM會對全部的對象維護一個獨一無二的類型標識,這就是Class對象。spa

Java的基本類型和關鍵字void也對應一個Class對象。
相同元素類型和維數的數組也對應一個Class對象。code

獲取Class對象的幾種方法:對象

  • 一個對象經過.class
  • 一個對象經過getClass方法
  • Class.forName(String)

Class對象有一些重要的方法:繼承

  • getConstructor系列,用於獲取構造方法
  • getField系列,用於獲取屬性
  • getMethod系列,用於獲取方法

上述系列中包含Declared系列,能夠獲取當前對象的全部類型的反射(包含private),可是不能獲取父類的反射接口

Constructor,Field,Method都是Class導演的三大利器,它們都位於java.lang.reflect包下,接下來將分別對這三大利器進行展現。get

Constructor

經過Class對象獲取到的Constructor對象以後,最經常使用的操做就是用來實例化對象,調用newInstance方法便可。string

其實在Class對象中也有一個newInstance方法,也能夠用來實例化對象,它們的區別是什麼呢?

  • Class.newInstance() 只可以調用無參的構造函數,即默認的構造函數,要求被調用的構造函數是可見的,也即必須是public類型的;
  • Constructor.newInstance() 能夠根據傳入的參數,調用任意構造構造函數,在特定的狀況下,能夠調用私有的構造函數。

Demo

//Class.newInstance()
//只能調用public屬性的無參構造函數 
A a = (A)Class.forName(A.class.getName()).newInstance();  

//Constructor.newInstance()
Class c= Class.forName(A.class.getName());
   
/*如下調用無參的、私有構造函數*/  
//得到無參構造
Constructor c0=c.getDeclaredConstructor();  
//設置無參構造是可訪問的
c0.setAccessible(true);   
A a0=(A)c0.newInstance();
    
//調用無參構造函數,生成對象實例
/*如下調用帶參的、私有構造函數*/   
Constructor c1=c.getDeclaredConstructor(new Class[]{int.class,int.class});   
c1.setAccessible(true);   
//調用有參構造函數,生成對象實例 
A a1=(A)c1.newInstance(new Object[]{5,6});   

複製代碼

使用場景:

若是使用接口模式,使用new建立一個門的對象,Door door = new WoodenDoor(),當以換成其餘門,須要修改代碼,Door door = new OtherDoor()。因此咱們須要使用工廠模式,須要什麼門就生產什麼門,若是咱們再使用newInstance()方法來生產,則只須要修改配置文件便可。

Field

經過Class對象獲取到的Field對象以後,咱們就能夠自由的查看和設置對象的屬性值。

關鍵方法:

  • get(Object object)查看特定對象的屬性值
  • set(Object object, Object value)給特定對象設置屬性
  • setAccessible(boolean flag)讓private成員擁有public權限

Demo

//獲取Class對象
Class aClass = MyObject.class
//經過Class對象獲取Field對象
Field field = aClass.getDeclaredField("someField");
//設置可訪問權限
field.setAccessible(true);

MyObject objectInstance = new MyObject();

//獲取特定的對象的變量屬性值
Object value = field.get(objectInstance);
//給特定對象的變量設置屬性
field.set(objetInstance, value);
複製代碼

由於Constructor,Field,Method都繼承自AccessibleObject類,全部都擁有setAccessible方法,我的感受setAccessible有點窺探隱私的感受,哈哈哈,不知道Class導演怎麼看。

Method

經過Class對象獲取到的Method對象以後,想都不用想啊,獲取一個方法不調用它幹嗎,因此咱們最經常使用的操做應該就是invoke方法。

相信你們對invoke並不會陌生,由於不少的異常,最後都會定位到invoke方法。

java.lang.NullPointerException
  at ......
  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  at java.lang.reflect.Method.invoke(Method.java:497)
複製代碼

invoke(Object receiver, Object… args),能夠分爲兩種,傳遞receiver參數表示調用特定對象的方法,傳null表示調用靜態方法。

//獲取一個方法名爲doSomesthing,參數類型爲String的方法
Method method = MyObject.class.getMethod("doSomething", String.class);

//調用靜態的doSomesthing方法,傳遞參數"parameter-value1"
Object returnValue = method.invoke(null, "parameter-value1");
複製代碼

最後說兩句

本文只是對Clss對象以及reflect包下的對象進行簡單的使用說明,關於反射的實現和原理,還有待於深刻研究。例如AccessibleObject對象以及對應相關xxxAccessor的實現。

相關文章
相關標籤/搜索