反射知識(shí)點(diǎn)總匯
點(diǎn)擊上方“程序員大白”,選擇“星標(biāo)”公眾號(hào)
重磅干貨,第一時(shí)間送達(dá)
一、概念
1.1 概念
簡(jiǎn)單說(shuō),JAVA反射機(jī)制是指在運(yùn)行態(tài)可直接操作任意類或?qū)ο蟮乃袑傩院头椒ǖ墓δ堋?/span>
1.2 反射的用途
在運(yùn)行時(shí)獲取任意對(duì)象所屬的類
Class> clazz = Class.forName(String className);在運(yùn)行時(shí)構(gòu)造任意類的對(duì)象
Object obj = clazz.newInstance();在運(yùn)行時(shí)獲取任意類所具有的成員變量和方法
field.set(Object obj, Object value);field.get(Object obj);在運(yùn)行時(shí)調(diào)用任意對(duì)象的方法 (最常見的需求,尤其是當(dāng)該方法是私有方法或者隱藏方法)
method.invoke(Object obj, Object... args);
反射還可以獲取類的其他信息,包含modifiers(下面會(huì)介紹),以及superclass, 實(shí)現(xiàn)的interfaces等。
針對(duì)動(dòng)態(tài)語(yǔ)言,大致認(rèn)同的一個(gè)定義是:“程序運(yùn)行時(shí),允許改變程序結(jié)構(gòu)或變量類型,這種語(yǔ)言稱為動(dòng)態(tài)語(yǔ)言”。反射機(jī)制在運(yùn)行時(shí)只能調(diào)用methods或改變fields內(nèi)容,卻無(wú)法修改程序結(jié)構(gòu)或變量類型。從這個(gè)觀點(diǎn)看,Perl,Python,Ruby是動(dòng)態(tài)語(yǔ)言,C++,Java,C#不是動(dòng)態(tài)語(yǔ)言。
二、反射
2.1 核心類
java.lang.Class:代表類
java.lang.reflect.Constructor: 代表類的構(gòu)造方法
java.lang.reflect.Field: 代表類的屬性
java.lang.reflect.Method: 代表類的方法
java.lang.reflect.Modifier:代表類、方法、屬性的描述修飾符。
其中Modifier取值范圍如下:
public, protected, private, abstract, static, final, transient, volatile, synchronized, native, strictfp, interface。
Constructor, Field, Method這三個(gè)類都繼承AccessibleObject,該對(duì)象有一個(gè)非常重要的方法setAccessible(boolean flag), 借助該方法,能直接調(diào)用非Public的屬性與方法。
2.2 核心方法
1.成員屬性(Field):
1getFields():獲得類的public類型的屬性。
2getDeclaredFields():獲得類的所有屬性。
3getField(String?name)
4getDeclaredField(String?name):獲取類的特定屬性2.成員方法(Method):
1getMethods():獲得類的public類型的方法。
2getDeclaredMethods():獲得類的所有方法。
3getMethod(String?name,?Class[] parameterTypes):獲得類的特定方法
4getDeclaredMethod(String?name,?Class[] parameterTypes):獲得類的特定方法3.構(gòu)造方法(Constructor):
1getConstructors():獲得類的public類型的構(gòu)造方法。
2getDeclaredConstructors():獲得類的所有構(gòu)造方法。
3getConstructor(Class[] parameterTypes):獲得類的特定構(gòu)造方法
4getDeclaredConstructor(Class[] params);獲得類的特定方法
2.3 深入Class類
Java所有的類都是繼承于Oject類,其內(nèi)聲明了多個(gè)應(yīng)該被所有Java類覆寫的方法:hashCode()、equals()、clone()、toString()、notify()、wait()、getClass()等,其中g(shù)etClass返回的便是一個(gè)Class類的對(duì)象。Class類也同樣是繼承Object類,擁有相應(yīng)的方法。
Java程序在運(yùn)行時(shí),運(yùn)行時(shí)系統(tǒng)對(duì)每一個(gè)對(duì)象都有一項(xiàng)類型標(biāo)識(shí),用于記錄對(duì)象所屬的類。虛擬機(jī)使用運(yùn)行時(shí)類型來(lái)選擇相應(yīng)方法去執(zhí)行,保存所有對(duì)象類型信息的類便是Class類。
Class類沒有公共構(gòu)造方法,Class對(duì)象是在加載類時(shí)由 Java 虛擬機(jī)以及通過(guò)調(diào)用ClassLoader的defineClass 方法自動(dòng)構(gòu)造的,因此不能顯式地聲明一個(gè)Class對(duì)象。
虛擬機(jī)為每種類型管理一個(gè)獨(dú)一無(wú)二的Class對(duì)象。也就是說(shuō),每個(gè)類(型)都有一個(gè)Class對(duì)象。運(yùn)行程序時(shí),Java虛擬機(jī)(JVM)首先檢查是否所要加載的類對(duì)應(yīng)的Class對(duì)象是否已經(jīng)加載。如果沒有加載,JVM就會(huì)根據(jù)類名查找.class文件,并將其Class對(duì)象載入。
基本的 Java 類型(boolean、byte、char、short、int、long、float 和 double)和關(guān)鍵字 void 也都對(duì)應(yīng)一個(gè) Class 對(duì)象。每個(gè)數(shù)組屬于被映射為 Class 對(duì)象的一個(gè)類,所有具有相同元素類型和維數(shù)的數(shù)組都共享該 Class 對(duì)象。一般某個(gè)類的Class對(duì)象被載入內(nèi)存,它就用來(lái)創(chuàng)建這個(gè)類的所有對(duì)象。
三、用處
1.如何通過(guò)反射獲取一個(gè)類?
Class.forName(String className); (最常用)

2.如何調(diào)用私有類,或者類的私有方法或?qū)傩裕?/span>
私有類:通過(guò)getDeclaredConstructor獲取constructor,再調(diào)用constructor.setAccessible(true);
私有方法:通過(guò)getDeclaredMethod獲取method,再調(diào)用method.setAccessible(true);
私有屬性:通過(guò)getDeclaredField獲取field,再調(diào)用field.setAccessible(true);
3.@hide標(biāo)記是做什么的,反射能否調(diào)用@hide標(biāo)記的類?
在Android的源碼中,我們會(huì)發(fā)現(xiàn)有很多被”@hide”標(biāo)記的類,它的作用是使這個(gè)類或方法在生成SDK時(shí)不可見。那么應(yīng)用程序便不可以直接調(diào)用。而反射機(jī)制可調(diào)用@hide標(biāo)記的類或方法,如入無(wú)人之地,暢通無(wú)阻。
4.如何通過(guò)反射調(diào)用內(nèi)部類?
假設(shè)com.reflect.Outer類有一個(gè)內(nèi)部類inner,調(diào)用方法如下:
1String?className?=?"com.reflect.Outer$inner";
2Class.forName(className);五、反射用法
前面介紹到,反射是為了在運(yùn)行態(tài)能操作類和對(duì)象,接下來(lái)重點(diǎn)介紹如何反射使用。
對(duì)于正常方式來(lái)調(diào)用方法,往往只需要一行到兩行代碼,即可完成相應(yīng)工作。而反射則顯得比較繁瑣,之所以繁瑣仍然使用反射方式,是因?yàn)榉瓷淠芨珊芏嗾?shí)例化對(duì)象的方式所無(wú)法做到的事。比如操作那些private的類、方法、屬性,以及@hide標(biāo)記過(guò)的類、方法、屬性。
為了到達(dá)即能有反射的功效,同時(shí)調(diào)用方法簡(jiǎn)單易用,寫了一個(gè)ReflectUtils類。對(duì)于方法調(diào)用,與正常對(duì)象的調(diào)用過(guò)程差不多。主要有以下4類需要用到反射的地方:
調(diào)用類的靜態(tài)方法
調(diào)用類的非靜態(tài)方法
set/get類的靜態(tài)屬性
set/get類的非靜態(tài)屬性
5.1?ReflectUtils類用法
調(diào)用流程一般為先獲取類或?qū)ο?,再調(diào)用相應(yīng)方法。針對(duì)上述4種需求,用法分別如下:
1. 調(diào)用類的靜態(tài)方法
對(duì)于參數(shù)方法,只需把參數(shù),緊跟方法名后面,可以跟不定長(zhǎng)的參數(shù)個(gè)數(shù)。
1Class>?clazz?=?ReflectUtils.getClazz("com.yuanhh.model.Outer");?//獲取class
2ReflectUtils.invokeStaticMethod(clazz,?"outerStaticMethod");??//無(wú)參方法
3ReflectUtils.invokeStaticMethod(clazz,?"outerStaticMethod","yuanhh");?//有參數(shù)方法2. 調(diào)用類的非靜態(tài)方法
1Object?obj?=?ReflectUtils.newInstance("com.yuanhh.model.Outer");??//實(shí)例化對(duì)象
2ReflectUtils.invokeMethod(obj,?"outerMethod");??//無(wú)參方法
3ReflectUtils.invokeMethod(obj,?"outerMethod",?"yuanhh");?//有參方法?**3.?set/get類的靜態(tài)屬性**?
4
5ReflectUtils.getStaticField(clazz,?"outerStaticField");?//get操作
6ReflectUtils.setStaticField(clazz,?"outerStaticField",?"new?value");?//set操作
4. set/get類的非靜態(tài)屬性
1ReflectUtils.getField(obj,?"outerField");??//get操作
2ReflectUtils.setField(obj,?"outerField",?"new?value");?//set操作如果只知道類名,需先查看該類的所有方法詳細(xì)參數(shù)信息,可以通過(guò)調(diào)用dumpClass(String className)?,返回值是String,記錄著所有構(gòu)造函數(shù),成員方法,屬性值的信息。
5.2 核心代碼
關(guān)于ReflectUtils類,列表部分核心方法。
先定義一個(gè)Outer類, 包名假設(shè)為com.yuanhh.model,對(duì)于該類,非public,構(gòu)造方法,成員方法,屬性都是private:
1class?Outer?{
2????private?String?outerField?=?"outer?Field";
3????private?static?String?outerStaticField?=?"outer?static?Field";
4
5????private?Outer(){
6????????System.out.println("I'am?outer?construction?without?args");
7????}
8
9????private?Outer(String?outerField){
10????????this.outerField?=?outerField;
11????????System.out.println("I'am?outer?construction?with?args?"+?this.outerField);
12????}
13
14????private?void?outerMethod(){
15????????System.out.println("I'am?outer?method");
16????}
17
18????private?void?outerMethod(String?param){
19????????System.out.println("I'am?outer?method?with?param?"+param);
20????}
21
22????private?static?void?outerStaticMethod(){
23????????System.out.println("I'am?outer?static?method");
24????}
25
26????private?static?void?outerStaticMethod(String?param){
27????????System.out.println("I'am?outer?static?method?with?param?"+param);
28????}
29}構(gòu)造函數(shù),獲取類的實(shí)例化對(duì)象:
1/**?*?實(shí)例化獲取類名對(duì)應(yīng)的類?*?*?@param?clazz?類?*?@param?constructorArgs?構(gòu)造函數(shù)的各個(gè)參數(shù)?*?@return?實(shí)例化對(duì)象?*/
2public?static?Object?newInstance(Class?clazz,?Object...?constructorArgs)?{
3????if?(clazz?==?null)?{
4????????return?null;
5????}
6
7????Object?object?=?null;
8
9????int?argLen?=?constructorArgs?==?null???0?:?constructorArgs.length;
10????Class>[]?parameterTypes?=?new?Class[argLen];
11????for?(int?i?=?0;?i?12????????parameterTypes[i]?=?constructorArgs[i].getClass();
13????}
14
15????try?{
16????????Constructor?constructor?=?clazz.getDeclaredConstructor(parameterTypes);
17????????if?(!constructor.isAccessible())?{
18????????????constructor.setAccessible(true);
19????????}
20????????object?=?constructor.newInstance(constructorArgs);
21
22????}?catch?(Exception?e)?{
23????????e.printStackTrace();
24????}
25
26????return?object;
27}對(duì)象方法的反射調(diào)用如下:
1/**?*?反射調(diào)用方法?*?*?@param?object?反射調(diào)用的對(duì)象實(shí)例?*?@param?methodName?反射調(diào)用的對(duì)象方法名?*?@param?methodArgs?反射調(diào)用的對(duì)象方法的參數(shù)列表?*?@return?反射調(diào)用執(zhí)行的結(jié)果?*/
2public?static?Object?invokeMethod(Object?object,?String?methodName,?Object...?methodArgs)?{
3????if?(object?==?null)?{
4????????return?null;
5????}
6
7????Object?result?=?null;
8????Class>?clazz?=?object.getClass();
9????try?{
10????????Method?method?=?clazz.getDeclaredMethod(methodName,?obj2class(methodArgs));
11????????if?(method?!=?null)?{
12????????????if?(!method.isAccessible())?{
13????????????????method.setAccessible(true);??//當(dāng)私有方法時(shí),設(shè)置可訪問
14????????????}
15????????????result?=?method.invoke(object,?methodArgs);
16????????}
17????}?catch?(Exception?e)?{
18????????e.printStackTrace();
19????}
20
21????return?result;
22
23}對(duì)象屬性值的反射獲取方法:
1/**?*?反射調(diào)用,獲取屬性值?*?*?@param?object?操作對(duì)象?*?@param?fieldName?對(duì)象屬性?*?@return?屬性值?*/
2public?static?Object?getField(Object?object,?String?fieldName)?{
3????if?(object?==?null)?{
4????????return?null;
5????}
6
7????Object?result?=?null;
8????Class>?clazz?=?object.getClass();
9????try?{
10????????Field?field?=?clazz.getDeclaredField(fieldName);
11????????if?(field?!=?null)?{
12????????????if?(!field.isAccessible())?{
13????????????????field.setAccessible(true);
14????????????}
15????????????result?=?field.get(object);
16????????}
17????}?catch?(Exception?e)?{
18????????e.printStackTrace();
19????}
20????return?result;
21}類屬性的反射設(shè)置過(guò)程:
1/**?*?反射調(diào)用,設(shè)置屬性值?*?*?@param?clazz?操作類?*?@param?fieldName?屬性名?*?@param?value?屬性的新值?*?@return?設(shè)置是否成功?*/
2public?static?boolean?setStaticField(Class?clazz,?String?fieldName,?Object?value)?{
3????if?(clazz?==?null)?{
4????????return?false;
5????}
6
7????Object?result?=?null;
8????try?{
9????????Field?field?=?clazz.getDeclaredField(fieldName);
10????????if?(field?!=?null)?{
11????????????if?(!field.isAccessible())?{
12????????????????field.setAccessible(true);
13????????????}
14????????????field.set(null,?value);
15????????}
16????}?catch?(Exception?e)?{
17????????e.printStackTrace();
18????????return?false;
19????}
20????return?true;
21}六、內(nèi)部類的反射用法
對(duì)于內(nèi)部類,這里比較復(fù)雜,而內(nèi)部類又分static內(nèi)部類與非static內(nèi)部類,兩者的反射方式還是有區(qū)別的,剛開始在這邊折騰了好一陣子,一直反射失敗。static內(nèi)部類與非static內(nèi)部類的反射調(diào)用,根本區(qū)別在于構(gòu)造方法不一樣。下面通過(guò)代碼來(lái)告訴如何正確。
6.1 static與非static內(nèi)部類的反射差異
先定義一個(gè)包含兩個(gè)內(nèi)部類的類:
1class?Outer?{
2????/**?*?普通內(nèi)部類?*/
3????class?Inner?{
4????????private?String?innerField?=?"inner?Field";
5
6????????private?Inner(){
7????????????System.out.println("I'am?Inner?construction?without?args");
8????????}
9
10????????private?Inner(String?innerField){
11????????????this.innerField?=?innerField;
12????????????System.out.println("I'am?Inner?construction?with?args?"+?this.innerField);
13????????}
14
15????????private?void?innerMethod(){
16????????????System.out.println("I'am?inner?method");
17????????}
18????}
19
20????/**?*?靜態(tài)內(nèi)部類?*/
21????static?class?StaticInner?{
22
23????????private?String?innerField?=?"StaticInner?Field";
24????????private?static?String?innerStaticField?=?"StaticInner?static?Field";
25
26????????private?StaticInner(){
27????????????System.out.println("I'am?StaticInner?construction?without?args");
28????????}
29
30????????private?StaticInner(String?innerField){
31????????????this.innerField?=?innerField;
32????????????System.out.println("I'am?StaticInner?construction?with?args?"+?this.innerField);
33????????}
34
35????????private?void?innerMethod(){
36????????????System.out.println("I'am?StaticInner?method");
37????????}
38
39????????private?static?void?innerStaticMethod(){
40????????????System.out.println("I'am?StaticInner?static?method");
41????????}
42????}
43}對(duì)于上面兩個(gè)內(nèi)部類,如果直接實(shí)例化內(nèi)部類,該怎么做,拋開private等權(quán)限不夠的問題,應(yīng)該是這樣的:
靜態(tài)內(nèi)部類:
Outer.StaticInner sInner = new Outer.StaticInner();非靜態(tài)內(nèi)部類:?
Outer.Inner inner = new Outer().new Inner();
這種差異,在于內(nèi)部類的構(gòu)造方法不一樣。我們可以通過(guò)下面的方法dumpClass()來(lái)比較。
1/**?*?獲取類的所有?構(gòu)造函數(shù),屬性,方法?*?*?@param?className?類名?*?@return?*/
2public?static?String?dumpClass(String?className)?{
3????StringBuffer?sb?=?new?StringBuffer();
4????Class>?clazz;
5????try?{
6????????clazz?=?Class.forName(className);
7????}?catch?(ClassNotFoundException?e)?{
8????????e.printStackTrace();
9????????return?"";
10????}
11
12????Constructor>[]?cs?=?clazz.getDeclaredConstructors();
13????sb.append("------?Constructor?------>?").append("\n");
14????for?(Constructor>?c?:?cs)?{
15????????sb.append(c.toString()).append("\n");
16????}
17
18????sb.append("------?Field?------>").append("\n");
19????Field[]?fs?=?clazz.getDeclaredFields();
20????for?(Field?f?:?fs)?{
21????????sb.append(f.toString()).append("\n");
22????????;
23????}
24????sb.append("------?Method?------>").append("\n");
25????Method[]?ms?=?clazz.getDeclaredMethods();
26????for?(Method?m?:?ms)?{
27????????sb.append(m.toString()).append("\n");
28????}
29????return?sb.toString();
30}通過(guò)dumpClass(),對(duì)比我們會(huì)發(fā)現(xiàn),
static內(nèi)部類的默認(rèn)構(gòu)造函數(shù):?
private void com.yuanhh.model.Outer$StaticInner.innerMethod()非static內(nèi)部類的默認(rèn)構(gòu)造函數(shù):?
private com.yuanhh.model.Outer$Inner(com.yuanhh.model.Outer),多了一個(gè)參數(shù)com.yuanhh.model.Outer,也就是說(shuō)非static內(nèi)部類保持了外部類的引用。從屬性,我們也會(huì)發(fā)現(xiàn)多了一個(gè)final屬性final com.yuanhh.model.Outer com.yuanhh.model.Outer$Inner.this$0,這正是用于存儲(chǔ)外部類的屬性值。
正是這差異,導(dǎo)致兩者的反射調(diào)用過(guò)程中構(gòu)造方法的使用不一樣。另外內(nèi)部類的類名使用采用$符號(hào),來(lái)連接外部類與內(nèi)部類,格式為outer$inner。
6.2 static內(nèi)部類的?ReflectUtils類用法
1. 調(diào)用類的靜態(tài)方法(先獲取類,再調(diào)用)
1Class>?clazz?=?ReflectUtils.getClazz("com.yuanhh.model.Outer$StaticInner");?//獲取class
2?ReflectUtils.invokeStaticMethod(clazz,?"innerStaticMethod");??//無(wú)參方法
3?ReflectUtils.invokeStaticMethod(clazz,?"innerStaticMethod","yuanhh");?//有參數(shù)方法2. 調(diào)用類的非靜態(tài)方法(先獲取對(duì)象,再調(diào)用)
1Object?obj?=?ReflectUtils.newInstance("com.yuanhh.model.Outer$StaticInner");??//實(shí)例化對(duì)象
2?ReflectUtils.invokeMethod(obj,?"innerMethod");??//無(wú)參方法
3?ReflectUtils.invokeMethod(obj,?"innerMethod",?"yuanhh");?//有參方法
3. set/get類的靜態(tài)屬性(先獲取類,再調(diào)用)
1?ReflectUtils.getStaticField(clazz,?"innerField");?//get操作
2?ReflectUtils.setStaticField(clazz,?"innerField",?"new?value");?//set操作4. set/get類的非靜態(tài)屬性(先獲取對(duì)象,再調(diào)用)
1ReflectUtils.getField(obj,?"innerField");??//get操作
2?ReflectUtils.setField(obj,?"innerField",?"new?value");?//set操作?### 2.2 非static內(nèi)部類的?`ReflectUtils`類用法?非static內(nèi)部類,不能定義靜態(tài)方法和靜態(tài)屬性,故操作只有兩項(xiàng):5. 調(diào)用類的非靜態(tài)方法(先獲取對(duì)象,再調(diào)用)
1//?獲取外部類實(shí)例,這是static內(nèi)部類所不需要的,注意點(diǎn)
2?Object?outObj?=?ReflectUtils.newInstance("com.yuanhh.model.Outer");?
3?Object?obj?=?ReflectUtils.newInstance("com.yuanhh.model.Outer$Inner",?outObj);??//實(shí)例化對(duì)象
4?ReflectUtils.invokeMethod(obj,?"innerMethod");??//無(wú)參方法
5?ReflectUtils.invokeMethod(obj,?"innerMethod",?"yuanhh");?//有參方法6. set/get類的非靜態(tài)屬性(先獲取對(duì)象,再調(diào)用)
1ReflectUtils.getField(obj,?"innerField");??//get操作
2?ReflectUtils.setField(obj,?"innerField",?"new?value");?//set操作七、小結(jié)
主要
ReflectUtils類的用法,只需要按1.1?ReflectUtils類用法的方式使用即可,比如反射調(diào)用方法,只需知道類與方法名,即可調(diào)用完成invokeMethod(Object object, String methodName)操作簡(jiǎn)單。static與非static內(nèi)部類的區(qū)別,在2.2與2.3這兩節(jié),對(duì)于內(nèi)部類差異僅僅在于傳遞參數(shù)多了一個(gè)$符號(hào),以及非static內(nèi)部類實(shí)例化需要加上外部類的實(shí)例。
ReflectUtils類,以及本文所有涉及的代碼,即將打包上傳。
source: //yuanfentiank789.github.io/2015/07/18/java-reflection
推薦閱讀
國(guó)產(chǎn)小眾瀏覽器因屏蔽視頻廣告,被索賠100萬(wàn)(后續(xù))
年輕人“不講武德”:因看黃片上癮,把網(wǎng)站和786名女主播起訴了
關(guān)于程序員大白
程序員大白是一群哈工大,東北大學(xué),西湖大學(xué)和上海交通大學(xué)的碩士博士運(yùn)營(yíng)維護(hù)的號(hào),大家樂于分享高質(zhì)量文章,喜歡總結(jié)知識(shí),歡迎關(guān)注[程序員大白],大家一起學(xué)習(xí)進(jìn)步!


