很多朋友在深入的接觸
JAVA
語言后就會發現這樣兩個詞:反射
(Reflection)
和內省
(Introspector)
,經常搞不清楚這到底是怎么回事,在什么場合下應用以及如何使用?今天把這二者放在一起介紹,因為它們二者是相輔相成的。
反射
相對而言,反射比內省更容易理解一點。用一句比較白的話來概括,反射就是讓你可以通過名稱來得到對象
(
類,屬性,方法
)
的技術。例如我們可以通過類名來生成一個類的實例;知道了方法名,就可以調用這個方法;知道了屬性名就可以訪問這個屬性的值。
還是寫兩個例子讓大家更直觀的了解反射的使用方法:
//
通過類名來構造一個類的實例
Class cls_str = Class.forName(
"java.lang.String"
);
//
上面這句很眼熟,因為使用過
JDBC
訪問數據庫的人都用過
J
Object str = cls_str.newInstance();
//
相當于
String str = new String();
|
//
通過方法名來調用一個方法
String methodName =
"length"
;
Method m = cls_str.getMethod(methodName,
null
);
System.out.println(
"length is "
+ m.invoke(str,
null
));
//
相當于
System.out.println(str.length());
|
上面的兩個例子是比較常用方法。看到上面的例子就有人要發問了:為什么要這么麻煩呢?本來一條語句就完成的事情干嗎要整這么復雜?沒錯,在上面的例子中確實沒有必要這么麻煩。不過你想像這樣一個應用程序,它支持動態的功能擴展,也就是說程序不重新啟動但是可以自動加載新的功能,這個功能使用一個具體類來表示。首先我們必須為這些功能定義一個接口類,然后我們要求所有擴展的功能類必須實現我指定的接口,這個規定了應用程序和可擴展功能之間的接口規則,但是怎么動態加載呢?我們必須讓應用程序知道要擴展的功能類的類名,比如是
test.Func1
,當我們把這個類名
(
字符串
)
告訴應用程序后,它就可以使用我們第一個例子的方法來加載并啟用新的功能。這就是類的反射,請問你有別的選擇嗎?
??????
關于方法的反射建議大家看我的另外一篇文章《
利用
Turbine
的事件映射來擴展
Struts
的功能
》,地址是:
http://www.javayou.com/article/CSDN/extend_struts.html
。這篇文章詳細介紹了如果通過反射來擴展
Struts
框架的功能。
內省
內省是
Java
語言對
Bean
類屬性、事件的一種缺省處理方法。例如類
A
中有屬性
name,
那我們可以通過
getName,setName
來得到其值或者設置新的值。通過
getName/setName
來訪問
name
屬性,這就是默認的規則。
Java
中提供了一套
API
用來訪問某個屬性的
getter/setter
方法,通過這些
API
可以使你不需要了解這個規則(但你最好還是要搞清楚),這些
API
存放于包
java.beans
中。
一般的做法是通過類
Introspector
來獲取某個對象的
BeanInfo
信息,然后通過
BeanInfo
來獲取屬性的描述器(
PropertyDescriptor
),通過這個屬性描述器就可以獲取某個屬性對應的
getter/setter
方法,然后我們就可以通過反射機制來調用這些方法。下面我們來看一個例子,這個例子把某個對象的所有屬性名稱和值都打印出來:
/*?
?* Created on 2004-6-29
?*/
package
demo;
import
java.beans.BeanInfo;
import
java.beans.Introspector;
import
java.beans.PropertyDescriptor;
/**
?
*
內省演示例子
?
*
@author
liudong
?
*/
public
class
IntrospectorDemo {
??? String name;
???
public
static
void
main(String[] args)
throws
Exception{
??????? IntrospectorDemo demo =
new
IntrospectorDemo();
??????? demo.setName(
"Winter Lau"
);
???????
???????
//
如果不想把父類的屬性也列出來的話,
???????
//
那
getBeanInfo
的第二個參數填寫父類的信息
??????? BeanInfo bi = Introspector.getBeanInfo(demo.getClass(),
Object.
class
);
??????? PropertyDescriptor[] props = bi.getPropertyDescriptors();
???????
for
(
int
i=0;i<props.length;i++){
??????????? System.out.println(props[i].getName()+
"="
+
??????????????????? props[i].getReadMethod().invoke(demo,
null
));
??????? }
??? }
???
???
public
String getName() {
???????
return
name;
??? }
???
public
void
setName(String name) {
???????
this
.name = name;
??? }
}
|
Web
開發框架
Struts
中的
FormBean
就是通過內省機制來將表單中的數據映射到類的屬性上,因此要求
FormBean
的每個屬性要有
getter/setter
方法。但也并不總是這樣,什么意思呢?就是說對一個
Bean
類來講,我可以沒有屬性,但是只要有
getter/setter
方法中的其中一個,那么
Java
的內省機制就會認為存在一個屬性,比如類中有方法
setMobile
,那么就認為存在一個
mobile
的屬性,這樣可以方便我們把
Bean
類通過一個接口來定義而不用去關心具體實現,不用去關心
Bean
中數據的存儲。比如我們可以把所有的
getter/setter
方法放到接口里定義,但是真正數據的存取則是在具體類中去實現,這樣可提高系統的擴展性。
總結
將
Java
的反射以及內省應用到程序設計中去可以大大的提供程序的智能化和可擴展性。有很多項目都是采取這兩種技術來實現其核心功能,例如我們前面提到的
Struts
,還有用于處理
XML
文件的
Digester
項目,其實應該說幾乎所有的項目都或多或少的采用這兩種技術。在實際應用過程中二者要相互結合方能發揮真正的智能化以及高度可擴展性。