1. 程式人生 > >學習筆記-基礎知識13-反射機制

學習筆記-基礎知識13-反射機制

反射機制


1.反射的概念
要是想得到物件真正的型別,就得使用反射。
反射機制:
反射機制指的是程式在執行時能夠獲取自身的資訊。
在java中,只要給定類的名字,那麼就可以通過反射機制來獲得類的所有資訊。
反射機制的優點與缺點:
反射機制的優點就是可以實現動態建立物件和編譯,體現出很大的靈活性。
缺點是對效能有影響。使用反射基本上是一種解釋操作,這類操作總是慢於只直接執行相同的操作。
Class類和Class類例項
Java程式中的各個Java類屬於同一類事物,描述這類事物的Java類就是Class類。
一個類被類載入器載入到記憶體中,佔用一片儲存空間,這個空間裡面的內容就是類的位元組碼,
不同的類的位元組碼是不同的,所以它們在記憶體中的內容是不同的;
用類來描述物件,用元資料來描述Class,反射就是得到元資料的行為。
一個類在虛擬機器中只有一份位元組碼;

2.獲得Class物件
3種方式:
a.呼叫某個類的class屬性獲取Class物件,
如Date.class會返回Date類對應的Class物件(其實就是得到一個類的一份位元組碼檔案);
b.使用Class類的forName(String className)靜態方法,className表示全限定名;
如String的全限定名:java.lang.String;
c.呼叫某個物件的getClass()方法。該方法屬於Object類;
Class<?> clz = new Date().getClass();
獲取Class 物件最常用的是利用屬性的方法!


3.九個預定義Class物件
基本的Java型別(boolean、byte、char、short、int、long、float 和double)
和關鍵字void通過class屬性也表示為Class物件;
Class類中boolean isPrimitive():判定指定的Class物件是否表示一個基本型別。
包裝類和Void類的靜態TYPE欄位;
包裝類都有一個常量TYPE,用來表示其基本資料型別的位元組碼
Integer.TYPE == int.class ;
Integer.class == int.class;
陣列型別的Class例項物件:
Class<String[]> clz = String[].class;
Class類中boolean isArray():判定此Class物件是否表示一個數組型別。


4.利用Class獲取類的屬性資訊
Class<BaseDemo> c = BaseDemo.class;
c:類可以,介面也可以
c.getPackage();得到包名
c.getName();得到全限定名
c.getSimpleName();得到類的簡稱
c.getSuperclass().getSimpleName();得到父類
c.getInterfaces();得到介面


5.Class中得到構造方法Constructor、方法Method、欄位Field
常用方法:
Constructor類用於描述類中的構造方法:
Constructor<T> getConstructor(Class<?>... parameterTypes)
返回該Class物件表示類的指定的public構造方法;
Constructor<?>[] getConstructors()
返回該Class物件表示類的所有public構造方法;
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
返回該Class物件表示類的指定的構造方法,和訪問許可權無關;
Constructor<?>[] getDeclaredConstructors()
返回該Class物件表示類的所有構造方法,和訪問許可權無關;
Method類用於描述類中的方法:
Method getMethod(String name, Class<?> ... parameterTypes)
返回該Class物件表示類和其父類的指定的public方法;
Method[] getMethods():
返回該Class物件表示類和其父類的所有public方法;
Method getDeclaredMethod(String name, Class<?>... parameterTypes)
返回該Class物件表示類的指定的方法。和訪問許可權無關,但不包括繼承的方法;
Method[] getDeclaredMethods(): 
獲得類所有的方法,包括公共、保護、預設(包)訪問和私有方法,但不包括繼承的方法;


對於方法,欄位,構造方法之類用類獲取記住四個:
獲取全部,獲取特定,暴力獲取全部,暴力獲取特定!

6.利用反射建立物件
建立物件:
方法1.使用Class物件的newInstance()方法建立該Class物件的例項,
此時該Class物件必須要有無引數的構造方法。
方法2.使用Class物件獲取指定的Constructor物件,
再呼叫Constructor的newInstance()方法建立物件類的例項,
此時可以選擇使用某個構造方法。如果這個構造方法被私有化起來,
那麼必須先申請訪問,將可以訪問設定為true;
總結步驟:
a.獲取該類的Class物件。
b.利用Class物件的getConstructor()方法來獲取指定的構造方法。
c.申請訪問(設定為可訪問)
AccessibleObject物件的setAccessible(boolean flag)方法,
當flag為true的時候,就會忽略訪問許可權(可訪問私有的成員)。
d.呼叫Constructor(構造方法)的newInstance()方法建立物件。


7.使用反射呼叫方法
每個Method的物件對應一個具體的底層方法。
獲得Method物件後,程式可以使用Method裡面的invoke方法來執行該底層方法。
Object invoke(Object obj,Object ... args):
其中:obj表示呼叫底層方法的物件,
args表示傳遞的實際引數。
若底層方法是靜態的,那麼可以忽略指定的obj引數。該引數可以為null。
若底層方法所需的形參個數為0,則所提供的args陣列長度可以為0 或null。
不寫,null或new Object[]{}
若底層方法返回的是陣列型別,invoke方法返回的是底層方法的返回型別;


8.使用反射呼叫可變引數方法
使用反射呼叫可變引數方法,要把可變引數都當做是其對應的陣列型別引數;
如show(XX... is)作為show(XX[] is)呼叫;
不管可變引數元素型別是引用型別,都使用Object[]封裝一層,再傳遞,保證無誤。


9.使用反射操作欄位
Field提供兩組方法操作欄位:
xxx getXxx(Object obj):獲取obj物件該Field的欄位值,此處的xxx表示8 個基本資料型別。
若該欄位的型別是引用資料型別則使用Object get(Object obj);
void setXxx(Object obj,xxx val):將obj物件的該Field欄位設定成val值。
若該欄位的型別是引用資料型別則使用void set(Object obj, Object value);


10.反射來獲取泛型資訊
對普通Field:
通過指定對應的Class物件,程式可以獲得該類裡面所有的Field,
不管該Field使用private,public。
獲得Field物件後使用getType()來獲取其型別。
Class<?> type = f.getType();//獲得欄位的型別
若該Field有泛型修飾:
Map<String,Integer>;
通過Type gType = f.getGenericType()得到泛型型別
將Type物件強轉為ParameterizedType,其表示增加泛型後的型別
Type getRawType()返回被泛型限制的型別;
Type[] getActualTypeArguments()返回泛型引數型別;
利用反射來獲取泛型的型別(泛型資訊)步驟:
獲取當前類
獲取目標欄位
獲取包含泛型型別的型別getGenericType()
強轉至子類ParameterizedType 因為Type 沒有任何對應的方法
獲得泛型真正的型別getActualTypeArguments()