Java
?Reflection?(JAVA反射)????
Reflection?是?Java?程序開(kāi)發(fā)語(yǔ)言的特征之一,它允許運(yùn)行中的?Java?程序?qū)ψ陨磉M(jìn)行檢查,或者說(shuō)“自審”,并能直接操作程序的內(nèi)部屬性。例如,使用它能獲得?Java?類(lèi)中各成員的名稱(chēng)并顯示出來(lái)。
Java?的這一能力在實(shí)際應(yīng)用中也許用得不是很多,但是在其它的程序設(shè)計(jì)語(yǔ)言中根本就不存在這一特性。例如,Pascal、C?或者?C++?中就沒(méi)有辦法在程序中獲得函數(shù)定義相關(guān)的信息。
JavaBean?是?reflection?的實(shí)際應(yīng)用之一,它能讓一些工具可視化的操作軟件組件。這些工具通過(guò)?reflection?動(dòng)態(tài)的載入并取得?Java?組件(類(lèi))?的屬性。
1.?一個(gè)簡(jiǎn)單的例子
考慮下面這個(gè)簡(jiǎn)單的例子,讓我們看看?reflection?是如何工作的。
import?java.lang.reflect.*;
public?class?DumpMethods?{
???public?static?void?main(String?args[])?{
???????try?{
???????????Class?c?=?Class.forName(args[0]);
???????????Method?m[]?=?c.getDeclaredMethods();
???????????for?(int?i?=?0;?i?<?m.length;?i++)
???????????????System.out.println(m[i].toString());
???????}?catch?(Throwable?e)?{
???????????System.err.println(e);
???????}
???}
}
按如下語(yǔ)句執(zhí)行:
java?DumpMethods?java.util.Stack
它的結(jié)果輸出為:
public?java.lang.Object?java.util.Stack.push(java.lang.Object)
public?synchronized?java.lang.Object?java.util.Stack.pop()
public?synchronized?java.lang.Object?java.util.Stack.peek()
public?boolean?java.util.Stack.empty()
public?synchronized?int?java.util.Stack.search(java.lang.Object)
這樣就列出了java.util.Stack?類(lèi)的各方法名以及它們的限制符和返回類(lèi)型。
這個(gè)程序使用?Class.forName?載入指定的類(lèi),然后調(diào)用?getDeclaredMethods?來(lái)獲取這個(gè)類(lèi)中定義了的方法列表。java.lang.reflect.Methods?是用來(lái)描述某個(gè)類(lèi)中單個(gè)方法的一個(gè)類(lèi)。
2.開(kāi)始使用?Reflection
用于?reflection?的類(lèi),如?Method,可以在?java.lang.relfect?包中找到。使用這些類(lèi)的時(shí)候必須要遵循三個(gè)步驟:第一步是獲得你想操作的類(lèi)的?java.lang.Class?對(duì)象。在運(yùn)行中的?Java?程序中,用?java.lang.Class?類(lèi)來(lái)描述類(lèi)和接口等。
下面就是獲得一個(gè)?Class?對(duì)象的方法之一:
Class?c?=?Class.forName("java.lang.String");
這條語(yǔ)句得到一個(gè)?String?類(lèi)的類(lèi)對(duì)象。還有另一種方法,如下面的語(yǔ)句:
Class?c?=?int.class;
或者
Class?c?=?Integer.TYPE;
它們可獲得基本類(lèi)型的類(lèi)信息。其中后一種方法中訪(fǎng)問(wèn)的是基本類(lèi)型的封裝類(lèi)?(如?Integer)?中預(yù)先定義好的?TYPE?字段。
第二步是調(diào)用諸如?getDeclaredMethods?的方法,以取得該類(lèi)中定義的所有方法的列表。
一旦取得這個(gè)信息,就可以進(jìn)行第三步了??使用?reflection?API?來(lái)操作這些信息,如下面這段代碼:
Class?c?=?Class.forName("java.lang.String");
Method?m[]?=?c.getDeclaredMethods();
System.out.println(m[0].toString());
它將以文本方式打印出?String?中定義的第一個(gè)方法的原型。
在下面的例子中,這三個(gè)步驟將為使用?reflection?處理特殊應(yīng)用程序提供例證。
模擬?instanceof?操作符
得到類(lèi)信息之后,通常下一個(gè)步驟就是解決關(guān)于?Class?對(duì)象的一些基本的問(wèn)題。例如,Class.isInstance?方法可以用于模擬?instanceof?操作符:
class?A?{
}
public?class?instance1?{
???public?static?void?main(String?args[])?{
???????try?{
???????????Class?cls?=?Class.forName("A");
???????????boolean?b1?=?cls.isInstance(new?Integer(37));
???????????System.out.println(b1);
???????????boolean?b2?=?cls.isInstance(new?A());
???????????System.out.println(b2);
???????}?catch?(Throwable?e)?{
???????????System.err.println(e);
???????}
???}
}
在這個(gè)例子中創(chuàng)建了一個(gè)?A?類(lèi)的?Class?對(duì)象,然后檢查一些對(duì)象是否是?A?的實(shí)例。Integer(37)?不是,但?new?A()?是。
3.找出類(lèi)的方法
找出一個(gè)類(lèi)中定義了些什么方法,這是一個(gè)非常有價(jià)值也非常基礎(chǔ)的?reflection?用法。下面的代碼就實(shí)現(xiàn)了這一用法:
import?java.lang.reflect.*;
public?class?method1?{
???private?int?f1(Object?p,?int?x)?throws?NullPointerException?{
???????if?(p?==?null)
???????????throw?new?NullPointerException();
???????return?x;
???}
???public?static?void?main(String?args[])?{
???????try?{
???????????Class?cls?=?Class.forName("method1");
???????????Method?methlist[]?=?cls.getDeclaredMethods();
???????????for?(int?i?=?0;?i?<?methlist.length;?i++)?{
???????????????Method?m?=?methlist[i];
???????????????System.out.println("name?=?"?+?m.getName());
???????????????System.out.println("decl?class?=?"?+?m.getDeclaringClass());
???????????????Class?pvec[]?=?m.getParameterTypes();
???????????????for?(int?j?=?0;?j?<?pvec.length;?j++)
???????????????????System.out.println("param?#"?+?j?+?"?"?+?pvec[j]);
???????????????Class?evec[]?=?m.getExceptionTypes();
???????????????for?(int?j?=?0;?j?<?evec.length;?j++)
???????????????????System.out.println("exc?#"?+?j?+?"?"?+?evec[j]);
???????????????System.out.println("return?type?=?"?+?m.getReturnType());
???????????????System.out.println("-----");
???????????}
???????}?catch?(Throwable?e)?{
???????????System.err.println(e);
???????}
???}
}
這個(gè)程序首先取得?method1?類(lèi)的描述,然后調(diào)用?getDeclaredMethods?來(lái)獲取一系列的?Method?對(duì)象,它們分別描述了定義在類(lèi)中的每一個(gè)方法,包括?public?方法、protected?方法、package?方法和?private?方法等。如果你在程序中使用?getMethods?來(lái)代替?getDeclaredMethods,你還能獲得繼承來(lái)的各個(gè)方法的信息。
取得了?Method?對(duì)象列表之后,要顯示這些方法的參數(shù)類(lèi)型、異常類(lèi)型和返回值類(lèi)型等就不難了。這些類(lèi)型是基本類(lèi)型還是類(lèi)類(lèi)型,都可以由描述類(lèi)的對(duì)象按順序給出。
輸出的結(jié)果如下:
name?=?f1
decl?class?=?class?method1
param?#0?class?java.lang.Object
param?#1?int
exc?#0?class?java.lang.NullPointerException
return?type?=?int
-----
name?=?main
decl?class?=?class?method1
param?#0?class?[Ljava.lang.String;
return?type?=?void
-----
4.獲取構(gòu)造器信息
獲取類(lèi)構(gòu)造器的用法與上述獲取方法的用法類(lèi)似,如:
import?java.lang.reflect.*;
public?class?constructor1?{
???public?constructor1()?{
???}
???protected?constructor1(int?i,?double?d)?{
???}
???public?static?void?main(String?args[])?{
???????try?{
???????????Class?cls?=?Class.forName("constructor1");
???????????Constructor?ctorlist[]?=?cls.getDeclaredConstructors();
???????????for?(int?i?=?0;?i?<?ctorlist.length;?i++)?{
???????????????Constructor?ct?=?ctorlist[i];
???????????????System.out.println("name?=?"?+?ct.getName());
???????????????System.out.println("decl?class?=?"?+?ct.getDeclaringClass());
???????????????Class?pvec[]?=?ct.getParameterTypes();
???????????????for?(int?j?=?0;?j?<?pvec.length;?j++)
???????????????????System.out.println("param?#"?+?j?+?"?"?+?pvec[j]);
???????????????Class?evec[]?=?ct.getExceptionTypes();
???????????????for?(int?j?=?0;?j?<?evec.length;?j++)
???????????????????System.out.println("exc?#"?+?j?+?"?"?+?evec[j]);
???????????????System.out.println("-----");
???????????}
???????}?catch?(Throwable?e)?{
???????????System.err.println(e);
???????}
???}
}
這個(gè)例子中沒(méi)能獲得返回類(lèi)型的相關(guān)信息,那是因?yàn)闃?gòu)造器沒(méi)有返回類(lèi)型。
這個(gè)程序運(yùn)行的結(jié)果是:
name?=?constructor1
decl?class?=?class?constructor1
-----
name?=?constructor1
decl?class?=?class?constructor1
param?#0?int
param?#1?double
-----
5.獲取類(lèi)的字段(域)
找出一個(gè)類(lèi)中定義了哪些數(shù)據(jù)字段也是可能的,下面的代碼就在干這個(gè)事情:
import?java.lang.reflect.*;
public?class?field1?{
???private?double?d;
???public?static?final?int?i?=?37;
???String?s?=?"testing";
???public?static?void?main(String?args[])?{
???????try?{
???????????Class?cls?=?Class.forName("field1");
???????????Field?fieldlist[]?=?cls.getDeclaredFields();
???????????for?(int?i?=?0;?i?<?fieldlist.length;?i++)?{
???????????????Field?fld?=?fieldlist[i];
???????????????System.out.println("name?=?"?+?fld.getName());
???????????????System.out.println("decl?class?=?"?+?fld.getDeclaringClass());
???????????????System.out.println("type?=?"?+?fld.getType());
???????????????int?mod?=?fld.getModifiers();
???????????????System.out.println("modifiers?=?"?+?Modifier.toString(mod));
???????????????System.out.println("-----");
???????????}
???????}?catch?(Throwable?e)?{
???????????System.err.println(e);
???????}
???}
}
這個(gè)例子和前面那個(gè)例子非常相似。例中使用了一個(gè)新東西?Modifier,它也是一個(gè)?reflection?類(lèi),用來(lái)描述字段成員的修飾語(yǔ),如“private?int”。這些修飾語(yǔ)自身由整數(shù)描述,而且使用?Modifier.toString?來(lái)返回以“官方”順序排列的字符串描述?(如“static”在“final”之前)。這個(gè)程序的輸出是:
name?=?d
decl?class?=?class?field1
type?=?double
modifiers?=?private
-----
name?=?i
decl?class?=?class?field1
type?=?int
modifiers?=?public?static?final
-----
name?=?s
decl?class?=?class?field1
type?=?class?java.lang.String
modifiers?=
-----
和獲取方法的情況一下,獲取字段的時(shí)候也可以只取得在當(dāng)前類(lèi)中申明了的字段信息?(getDeclaredFields),或者也可以取得父類(lèi)中定義的字段?(getFields)?。
6.根據(jù)方法的名稱(chēng)來(lái)執(zhí)行方法
文本到這里,所舉的例子無(wú)一例外都與如何獲取類(lèi)的信息有關(guān)。我們也可以用?reflection?來(lái)做一些其它的事情,比如執(zhí)行一個(gè)指定了名稱(chēng)的方法。下面的示例演示了這一操作:
import?java.lang.reflect.*;
public?class?method2?{
???public?int?add(int?a,?int?b)?{
???????return?a?+?b;
???}
???public?static?void?main(String?args[])?{
???????try?{
???????????Class?cls?=?Class.forName("method2");
???????????Class?partypes[]?=?new?Class[2];
???????????partypes[0]?=?Integer.TYPE;
???????????partypes[1]?=?Integer.TYPE;
???????????Method?meth?=?cls.getMethod("add",?partypes);
???????????method2?methobj?=?new?method2();
???????????Object?arglist[]?=?new?Object[2];
???????????arglist[0]?=?new?Integer(37);
???????????arglist[1]?=?new?Integer(47);
???????????Object?retobj?=?meth.invoke(methobj,?arglist);
???????????Integer?retval?=?(Integer)?retobj;
???????????System.out.println(retval.intvalue());
???????}?catch?(Throwable?e)?{
???????????System.err.println(e);
???????}
???}
}
假如一個(gè)程序在執(zhí)行的某處的時(shí)候才知道需要執(zhí)行某個(gè)方法,這個(gè)方法的名稱(chēng)是在程序的運(yùn)行過(guò)程中指定的?(例如,JavaBean?開(kāi)發(fā)環(huán)境中就會(huì)做這樣的事),那么上面的程序演示了如何做到。
上例中,getMethod?用于查找一個(gè)具有兩個(gè)整型參數(shù)且名為?add?的方法。找到該方法并創(chuàng)建了相應(yīng)的?Method?對(duì)象之后,在正確的對(duì)象實(shí)例中執(zhí)行它。執(zhí)行該方法的時(shí)候,需要提供一個(gè)參數(shù)列表,這在上例中是分別包裝了整數(shù)?37?和?47?的兩個(gè)?Integer?對(duì)象。執(zhí)行方法的返回的同樣是一個(gè)?Integer?對(duì)象,它封裝了返回值?84。
7.創(chuàng)建新的對(duì)象
對(duì)于構(gòu)造器,則不能像執(zhí)行方法那樣進(jìn)行,因?yàn)閳?zhí)行一個(gè)構(gòu)造器就意味著創(chuàng)建了一個(gè)新的對(duì)象?(準(zhǔn)確的說(shuō),創(chuàng)建一個(gè)對(duì)象的過(guò)程包括分配內(nèi)存和構(gòu)造對(duì)象)。所以,與上例最相似的例子如下:
import?java.lang.reflect.*;
public?class?constructor2?{
???public?constructor2()?{
???}
???public?constructor2(int?a,?int?b)?{
???????System.out.println("a?=?"?+?a?+?"?b?=?"?+?b);
???}
???public?static?void?main(String?args[])?{
???????try?{
???????????Class?cls?=?Class.forName("constructor2");
???????????Class?partypes[]?=?new?Class[2];
???????????partypes[0]?=?Integer.TYPE;
???????????partypes[1]?=?Integer.TYPE;
???????????Constructor?ct?=?cls.getConstructor(partypes);
???????????Object?arglist[]?=?new?Object[2];
???????????arglist[0]?=?new?Integer(37);
???????????arglist[1]?=?new?Integer(47);
???????????Object?retobj?=?ct.newInstance(arglist);
???????}?catch?(Throwable?e)?{
???????????System.err.println(e);
???????}
???}
}
根據(jù)指定的參數(shù)類(lèi)型找到相應(yīng)的構(gòu)造函數(shù)并執(zhí)行它,以創(chuàng)建一個(gè)新的對(duì)象實(shí)例。使用這種方法可以在程序運(yùn)行時(shí)動(dòng)態(tài)地創(chuàng)建對(duì)象,而不是在編譯的時(shí)候創(chuàng)建對(duì)象,這一點(diǎn)非常有價(jià)值。
8.改變字段(域)的值
reflection?的還有一個(gè)用處就是改變對(duì)象數(shù)據(jù)字段的值。reflection?可以從正在運(yùn)行的程序中根據(jù)名稱(chēng)找到對(duì)象的字段并改變它,下面的例子可以說(shuō)明這一點(diǎn):
import?java.lang.reflect.*;
public?class?field2?{
???public?double?d;
???public?static?void?main(String?args[])?{
???????try?{
???????????Class?cls?=?Class.forName("field2");
???????????Field?fld?=?cls.getField("d");
???????????field2?f2obj?=?new?field2();
???????????System.out.println("d?=?"?+?f2obj.d);
???????????fld.setDouble(f2obj,?12.34);
???????????System.out.println("d?=?"?+?f2obj.d);
???????}?catch?(Throwable?e)?{
???????????System.err.println(e);
???????}
???}
}
這個(gè)例子中,字段?d?的值被變?yōu)榱?12.34。
9.使用數(shù)組
本文介紹的?reflection?的最后一種用法是創(chuàng)建的操作數(shù)組。數(shù)組在?Java?語(yǔ)言中是一種特殊的類(lèi)類(lèi)型,一個(gè)數(shù)組的引用可以賦給?Object?引用。觀察下面的例子看看數(shù)組是怎么工作的:
import?java.lang.reflect.*;
public?class?array1?{
???public?static?void?main(String?args[])?{
???????try?{
???????????Class?cls?=?Class.forName("java.lang.String");
???????????Object?arr?=?Array.newInstance(cls,?10);
???????????Array.set(arr,?5,?"this?is?a?test");
???????????String?s?=?(String)?Array.get(arr,?5);
???????????System.out.println(s);
???????}?catch?(Throwable?e)?{
???????????System.err.println(e);
???????}
???}
}
例中創(chuàng)建了?10?個(gè)單位長(zhǎng)度的?String?數(shù)組,為第?5?個(gè)位置的字符串賦了值,最后將這個(gè)字符串從數(shù)組中取得并打印了出來(lái)。
下面這段代碼提供了一個(gè)更復(fù)雜的例子:
import?java.lang.reflect.*;
public?class?array2?{
???public?static?void?main(String?args[])?{
???????int?dims[]?=?new?int[]{5,?10,?15};
???????Object?arr?=?Array.newInstance(Integer.TYPE,?dims);
???????Object?arrobj?=?Array.get(arr,?3);
???????Class?cls?=?arrobj.getClass().getComponentType();
???????System.out.println(cls);
???????arrobj?=?Array.get(arrobj,?5);
???????Array.setInt(arrobj,?10,?37);
???????int?arrcast[][][]?=?(int[][][])?arr;
???????System.out.println(arrcast[3][5][10]);
???}
}
例中創(chuàng)建了一個(gè)?5?x?10?x?15?的整型數(shù)組,并為處于?[3][5][10]?的元素賦了值為?37。注意,多維數(shù)組實(shí)際上就是數(shù)組的數(shù)組,例如,第一個(gè)?Array.get?之后,arrobj?是一個(gè)?10?x?15?的數(shù)組。進(jìn)而取得其中的一個(gè)元素,即長(zhǎng)度為?15?的數(shù)組,并使用?Array.setInt?為它的第?10?個(gè)元素賦值。
注意創(chuàng)建數(shù)組時(shí)的類(lèi)型是動(dòng)態(tài)的,在編譯時(shí)并不知道其類(lèi)型。
FeedBack:
# re: 轉(zhuǎn)載 Java Reflection (JAVA反射)
2007-09-28 11:04 | -274°C
@千里冰封
哎,我項(xiàng)目中也沒(méi)有用到這些技術(shù)。所以只停留在初步了解的層次。該文的學(xué)習(xí)我一般是一邊看一邊敲練習(xí)代碼。有誰(shuí)需要實(shí)例代碼 以及 《侯捷觀點(diǎn) java反射機(jī)制》PDF 文件 的朋友嗎? 回復(fù) 更多評(píng)論
哎,我項(xiàng)目中也沒(méi)有用到這些技術(shù)。所以只停留在初步了解的層次。該文的學(xué)習(xí)我一般是一邊看一邊敲練習(xí)代碼。有誰(shuí)需要實(shí)例代碼 以及 《侯捷觀點(diǎn) java反射機(jī)制》PDF 文件 的朋友嗎? 回復(fù) 更多評(píng)論
只有注冊(cè)用戶(hù)登錄后才能發(fā)表評(píng)論。 | ||
![]() |
||
網(wǎng)站導(dǎo)航:
博客園
IT新聞
Chat2DB
C++博客
博問(wèn)
管理
|
||
相關(guān)文章:
|
||
常用鏈接
留言簿(21)
隨筆分類(lèi)(265)
- android(1)
- c#(.net)(1)
- C++(33)
- DB(5)
- JAVA(38)
- JSP(17)
- linux(7)
- mac(10)
- nginx(3)
- PHP(20)
- python(19)
- UML(1)
- VML(1)
- WebService(2)
- web前端(71)
- XML(4)
- 感悟生活(5)
- 正則表達(dá)式(2)
- 計(jì)算機(jī)綜合(25)
隨筆檔案(242)
- 2012年5月 (1)
- 2012年4月 (2)
- 2011年12月 (9)
- 2011年11月 (20)
- 2011年3月 (8)
- 2010年10月 (15)
- 2010年6月 (2)
- 2010年4月 (1)
- 2010年3月 (7)
- 2010年1月 (4)
- 2009年12月 (1)
- 2009年10月 (1)
- 2009年9月 (1)
- 2009年8月 (8)
- 2009年6月 (11)
- 2009年5月 (3)
- 2009年4月 (7)
- 2008年11月 (5)
- 2008年10月 (6)
- 2008年8月 (1)
- 2008年7月 (6)
- 2008年6月 (5)
- 2008年5月 (1)
- 2008年4月 (18)
- 2008年3月 (1)
- 2008年2月 (1)
- 2007年12月 (9)
- 2007年9月 (5)
- 2007年8月 (6)
- 2007年7月 (4)
- 2007年6月 (6)
- 2007年5月 (8)
- 2007年4月 (9)
- 2007年3月 (6)
- 2007年2月 (44)
相冊(cè)
JAVA網(wǎng)站
關(guān)注的Blog
搜索
積分與排名
- 積分 - 914066
- 排名 - 40
最新評(píng)論

- 1.?re: IP地址獲取當(dāng)前地理位置接口
- fdf
- --fdfs
- 2.?re: 淺談comet技術(shù)
- slowhttp攻擊會(huì)直接把server搞垮 因?yàn)橥ǔ_@種應(yīng)用場(chǎng)景的http的timeout值會(huì)比較大
- --harlan
- 3.?re: php動(dòng)態(tài)圖片驗(yàn)證碼
- 額
- --人
- 4.?re: 關(guān)于傳值和傳引用的插曲
- 大家不要看這篇文章 本來(lái)很簡(jiǎn)單的事情反而被博主繞復(fù)雜了,
- --bandit