反射
反射庫(reflection library)提供了一個(gè)非常豐富且精心設(shè)計(jì)的工具集或舞,以便編寫能夠動(dòng)態(tài)操縱Java代碼的程序。
使用反射蒙幻,能在設(shè)計(jì)或者在添加新類的時(shí)候映凳,能夠快速應(yīng)用開發(fā)工具動(dòng)態(tài)的查詢新添加類的能力。
能夠分析類能力的程序稱為 反射 反射功能極其強(qiáng)大可以用來
- 在運(yùn)行時(shí)分析類的能力
- 在運(yùn)行時(shí)查看對(duì)象
- 實(shí)現(xiàn)通用的數(shù)組來操作代碼
- 利用Method對(duì)象邮破,此對(duì)象很類似C++中的函數(shù)指針
1,前提介紹: Class 類
在程序運(yùn)行期間诈豌,Java運(yùn)行時(shí)系統(tǒng)始終為所有對(duì)象維護(hù)一個(gè)被稱為運(yùn)行時(shí)的類型標(biāo)識(shí),這個(gè)信息跟蹤著每個(gè)對(duì)象專屬的類抒和。
Class類就是用來專門保存這些類的信息矫渔,通過Object類中的getClass()方法得到每個(gè)類的Class類實(shí)例
Person p;
Class cl = p.getClass();
一個(gè)Class類可以用來保存一個(gè)類的屬性,如:
p.setName("ZhangSan");
System.out.print(cl.getName()+" "+p.getName());
此時(shí)程序會(huì)輸出
Person ZhangSan
Class的getName()方法可以得到這個(gè)類的類名。
獲得類的Class實(shí)例的第2種方法:Class.forName(類名)
String className = "com.henu.Person";
Class cl = Class.forName(className);
第三種方法摧莽,直接 類名.class
Class class1 = Person.class;
Class class2 = Random.class;
通過獲得的Class實(shí)例再創(chuàng)建類的實(shí)例
Class cl = p.getClass();
Perons p = cl.newInstance();
以上代碼在使用時(shí)要放在try catch代碼塊里庙洼,來處理捕獲的異常
2,利用反射分析類的能力,檢查類的結(jié)構(gòu)
java.lang.reflect包中有三個(gè)類
- Field 描述類的域 getType()方法可以返回描述域所屬類型的Class對(duì)象
- Method 描述類的方法
- Constructor 描述類的構(gòu)造器
這三個(gè)類都有一個(gè)getModifiers的方法镊辕,描述各自的訪問修飾符,public private final...等油够,
再通過Modifier類中的isPublic,isPrivate,isXxx方法對(duì)其進(jìn)行判斷.
getFields
getMethods
getConstructors
這三個(gè)方法分別返回類提供的public域,方法,構(gòu)造器數(shù)組征懈,其中包括超類的共有成員.getDeclareFields
getDeclareMethods
getDeclaredConstructors
這三個(gè)方法將返回類中聲明的全部域石咬,方法和構(gòu)造器,包括private,protect,但不包括超類的共有成員
在運(yùn)行時(shí)使用反射分析對(duì)象
接下來進(jìn)一步查看數(shù)據(jù)域的實(shí)際內(nèi)容給
Person p = new Person("張三",20);
Class cl = p.getClass();
Field name = cl.getDeclaredField("name");//獲得Person 類的name域;
Object value = name.get(p); // 此時(shí)value = "張三"
值的注意的一點(diǎn)是卖哎,若name域在Person類中是私有的碌补,代碼會(huì)拋出異常,我們需要對(duì)獲得的數(shù)據(jù)域進(jìn)行設(shè)置
Person p = new Person("張三",20);
Class cl = p.getClass();
Field name = cl.getDeclaredField("name");//獲得Person 類的name域;
name.setAccessible(true);
Object value = name.get(p); // 此時(shí)value = "張三"
setAccessible方法是AccessibleObject類中的一個(gè)方法,它是Field,Method,Constructor類的共有超類棉饶,是為了調(diào)試厦章,持久存儲(chǔ)和相似調(diào)試提供的.
若數(shù)據(jù)域是基本數(shù)據(jù)類型,可以使用域.getDouble(類的實(shí)例),getInt(類的實(shí)例),也可以繼續(xù)使用get( )方法照藻,反射機(jī)制會(huì)自動(dòng)的將這個(gè)域值裝箱袜啃,變成Double,Integer幸缕。
.set(obj,value)方法可以將obj對(duì)象的指定域進(jìn)行賦值
name.set(p,"李四")//此時(shí)p對(duì)象的name為李四
3,使用反射編寫泛型數(shù)組代碼
java.lang.reflect包中的Array類允許動(dòng)態(tài)的創(chuàng)建數(shù)組
以前我們學(xué)過Arrays類中有個(gè)copyOf()方法群发,可以用來拓展已經(jīng)滿了的數(shù)組.
Person[ ] team = new Person[10];
team = Arrays.copyOf(team,2*team.length);
我們可以用反射寫一個(gè)通用的拓展數(shù)組長度的方法晰韵,能拓展任意類型的數(shù)組。
首先我們寫一個(gè)錯(cuò)誤的例子
public static Object[] badCopyOf(Object[] a,int newLength) {
Object[] newArray = new Object[newLength];
System.arrayCopy(a,0,newArray,0,Math.min(a.length,newLength));//第四個(gè)參數(shù)是要從原數(shù)組復(fù)制元素的個(gè)數(shù)
return newArray;
}
為什么這個(gè)代碼是錯(cuò)的呢熟妓,因?yàn)檫@個(gè)函數(shù)返回的數(shù)組類型是對(duì)象數(shù)組類型(Object [])
一個(gè)對(duì)象數(shù)組不能轉(zhuǎn)化成特定的類的數(shù)組(Object[]->person[]),這樣做會(huì)產(chǎn)生異常雪猪,其實(shí),當(dāng)我們把Person[]轉(zhuǎn)化成 Object[] 再轉(zhuǎn)化成Person[]是可以的起愈,但是一個(gè)一開始就是Object[]的數(shù)組是不能轉(zhuǎn)的,我們可以通過下面這個(gè)方法
Object newArray = Array.newInstance(componentType,int newLength);
java.lang.reflect.Array中的靜態(tài)方法newInstance方法能夠構(gòu)造新的數(shù)組只恨,第一個(gè)參數(shù)是元素的類型,第二個(gè)是數(shù)組長度抬虽。所以現(xiàn)在我們需要獲得新數(shù)組的元素類型和數(shù)組長度官觅。
- 獲得數(shù)組的長度可以通過.getLength()或者Array.getLength(數(shù)組)獲得
- 獲得數(shù)組的元素類型可以通過Class類的getComponentType()獲得
所以我們現(xiàn)在要實(shí)現(xiàn)一個(gè)通用的拓展任意類型的方法要經(jīng)過3步
- 首先獲得數(shù)組的Class對(duì)象
- 確認(rèn)它是一個(gè)數(shù)組類型(通過Class類的isArray()方法)
- 使用Class類的getComponentType()方法確定數(shù)組的類型
public static Object goodCopyOf(Object a,int newLength) {
Class cl = p.getClass();
if (cl.isArray()) return null;//如果不是數(shù)組類型就結(jié)束
Class componentType = cl.getComponentType();//獲得數(shù)組的元素類型
int length = a.getLength();
Object newArray = Array.newInstance(componentType,length);
System.arrayCopy(a,0,newArray,0,Math.min(length,newLength));
return newArray;
}
這樣這個(gè)方法就可以用了來拓展任意類型的數(shù)組了
int[] a = {1,2,3,4,5};
a = (int [])goodCopyOf(a,10);
通過Method的invoke方法調(diào)用類的任意方法
兩個(gè)方法
Method method = Person.getClass().getMethod(方法名,方法的參數(shù)類型.class)
method.invoke(方法隸屬的類的實(shí)例,方法的參數(shù)值)
//動(dòng)態(tài)構(gòu)造InvokeTest類的實(shí)例
Class<?> classType = InvokeTest.class;
Object invokeTest = classType.newInstance();
//動(dòng)態(tài)構(gòu)造InvokeTest類的add(int num1, int num2)方法,標(biāo)記為addMethod的Method對(duì)象
Method addMethod = classType.getMethod("add", new Class[]{int.class, int.class});
//動(dòng)態(tài)構(gòu)造的Method對(duì)象invoke委托動(dòng)態(tài)構(gòu)造的InvokeTest對(duì)象阐污,執(zhí)行對(duì)應(yīng)形參的add方法
Object result = addMethod.invoke(invokeTest, new Object[]{1, 2});