作者:yonnie?文章來源:http://yonnie.blog.hexun.com/
Reflection 是 Java 程序開發語言的特征之一,它允許運行中的 Java 程序對自身進行檢查,或者說“自審”,并能直接操作程序的內部屬性。例如,使用它能獲得 Java 類中各成員的名稱并顯示出來。
Java 的這一能力在實際應用中也許用得不是很多,但是在其它的程序設計語言中根本就不存在這一特性。例如,Pascal、C 或者 C++ 中就沒有辦法在程序中獲得函數定義相關的信息。
JavaBean 是 reflection 的實際應用之一,它能讓一些工具可視化的操作軟件組件。這些工具通過 reflection 動態的載入并取得 Java 組件(類) 的屬性。
1、一個簡單的例子
考慮下面這個簡單的例子,讓我們看看 reflection 是如何工作的。
?1
import
?java.lang.reflect.
*
;
?2
?3
public
?
class
?DumpMethods?
{
?4
????
public
?
static
?
void
?main(String?args[])?
{
?5
????????
try
?
{
?6
????????????Class?c?
=
?Class.forName(args[
0
]);
?7
????????????Method?m[]?
=
?c.getDeclaredMethods();
?8
????????????
for
?(
int
?i?
=
?
0
;?i?
<
?m.length;?i
++
)
?9
????????????????System.out.println(m[i].toString());
10
????????}
?
catch
?(Throwable?e)?
{
11
????????????System.err.println(e);
12
????????}
13
????}
14
}
15
按如下語句執行:
java?DumpMethods?java.util.Stack
它的結果輸出為:
public
?
synchronized
?java.lang.Object?java.util.Stack.pop()
public
?java.lang.Object?java.util.Stack.push(java.lang.Object)
public
?
boolean
?java.util.Stack.empty()
public
?
synchronized
?java.lang.Object?java.util.Stack.peek()
public
?
synchronized
?
int
?java.util.Stack.search(java.lang.Object)
這樣就列出了java.util.Stack 類的各方法名以及它們的限制符和返回類型。
這個程序使用 Class.forName 載入指定的類,然后調用 getDeclaredMethods 來獲取這個類中定義了的方法列表。java.lang.reflect.Methods 是用來描述某個類中單個方法的一個類。
2、開始使用Reflection
用于 reflection 的類,如 Method,可以在 java.lang.relfect 包中找到。使用這些類的時候必須要遵循三個步驟:第一步是獲得你想操作的類的 java.lang.Class 對象。在運行中的 Java 程序中,用 java.lang.Class 類來描述類和接口等。
下面就是獲得一個 Class 對象的方法之一:
Class c = Class.forName("java.lang.String");
這條語句得到一個 String 類的類對象。還有另一種方法,如下面的語句:
Class c = int.class;
或者
Class c = Integer.TYPE;
它們可獲得基本類型的類信息。其中后一種方法中訪問的是基本類型的封裝類 (如 Integer) 中預先定義好的 TYPE 字段。
第二步是調用諸如 getDeclaredMethods 的方法,以取得該類中定義的所有方法的列表。
一旦取得這個信息,就可以進行第三步了——使用 reflection API 來操作這些信息,如下面這段代碼:
Class?c?
=
?Class.forName(
"
java.lang.String
"
);
Method?m[]?
=
?c.getDeclaredMethods();
System.out.println(m[
0
].toString());
它將以文本方式打印出 String 中定義的第一個方法的原型。
在下面的例子中,這三個步驟將為使用 reflection 處理特殊應用程序提供例證。
模擬 instanceof 操作符
得到類信息之后,通常下一個步驟就是解決關于 Class 對象的一些基本的問題。例如,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);
????????}
????}
}
在這個例子中創建了一個 A 類的 Class 對象,然后檢查一些對象是否是 A 的實例。Integer(37) 不是,但 new A() 是。
3、找出類的方法
找出一個類中定義了些什么方法,這是一個非常有價值也非常基礎的 reflection 用法。下面的代碼就實現了這一用法:
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);
????????}
????}
}
這個程序首先取得 method1 類的描述,然后調用 getDeclaredMethods 來獲取一系列的 Method 對象,它們分別描述了定義在類中的每一個方法,包括 public 方法、protected 方法、package 方法和 private 方法等。如果你在程序中使用 getMethods 來代替 getDeclaredMethods,你還能獲得繼承來的各個方法的信息。
取得了 Method 對象列表之后,要顯示這些方法的參數類型、異常類型和返回值類型等就不難了。這些類型是基本類型還是類類型,都可以由描述類的對象按順序給出。
輸出的結果如下:
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、獲取構造器信息
獲取類構造器的用法與上述獲取方法的用法類似,如:
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);
????????}
????}
}
這個例子中沒能獲得返回類型的相關信息,那是因為構造器沒有返回類型。
這個程序運行的結果是:
name?
=
?constructor1
decl?
class
?
=
?
class
?constructor1
-----
name?
=
?constructor1
decl?
class
?
=
?
class
?constructor1
param?#
0
?
int
param?#
1
?
double
-----
5、獲取類的字段(域)
找出一個類中定義了哪些數據字段也是可能的,下面的代碼就在干這個事情:
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);
????????}
????}
}
這個例子和前面那個例子非常相似。例中使用了一個新東西 Modifier,它也是一個 reflection 類,用來描述字段成員的修飾語,如“private int”。這些修飾語自身由整數描述,而且使用 Modifier.toString 來返回以“官方”順序排列的字符串描述 (如“static”在“final”之前)。這個程序的輸出是:
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?
=
-----
和獲取方法的情況一下,獲取字段的時候也可以只取得在當前類中申明了的字段信息 (getDeclaredFields),或者也可以取得父類中定義的字段 (getFields) 。
6、根據方法的名稱來執行方法
以上所舉的例子無一例外都與如何獲取類的信息有關。我們也可以用 reflection 來做一些其它的事情,比如執行一個指定了名稱的方法。下面的示例演示了這一操作:
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);
????????}
????}
}
假如一個程序在執行的某處的時候才知道需要執行某個方法,這個方法的名稱是在程序的運行過程中指定的 (例如,JavaBean 開發環境中就會做這樣的事),那么上面的程序演示了如何做到。
上例中,getMethod 用于查找一個具有兩個整型參數且名為 add 的方法。找到該方法并創建了相應的 Method 對象之后,在正確的對象實例中執行它。執行該方法的時候,需要提供一個參數列表,這在上例中是分別包裝了整數 37 和 47 的兩個 Integer 對象。執行方法的返回的同樣是一個 Integer 對象,它封裝了返回值 84。
7、創建新的對象
對于構造器,則不能像執行方法那樣進行,因為執行一個構造器就意味著創建了一個新的對象 (準確的說,創建一個對象的過程包括分配內存和構造對象)。所以,與上例最相似的例子如下:
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);
????????}
????}
}
根據指定的參數類型找到相應的構造函數并執行它,以創建一個新的對象實例。使用這種方法可以在程序運行時動態地創建對象,而不是在編譯的時候創建對象,這一點非常有價值。
8、改變字段(域)的值
reflection 的還有一個用處就是改變對象數據字段的值。reflection 可以從正在運行的程序中根據名稱找到對象的字段并改變它,下面的例子可以說明這一點:
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);
????????}
????}
}
這個例子中,字段 d 的值被變為了 12.34。
9、使用數組
本文介紹的 reflection 的最后一種用法是創建的操作數組。數組在 Java 語言中是一種特殊的類類型,一個數組的引用可以賦給 Object 引用。觀察下面的例子看看數組是怎么工作的:
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);
????????}
????}
}
例中創建了 10 個單位長度的 String 數組,為第 5 個位置的字符串賦了值,最后將這個字符串從數組中取得并打印了出來。
下面這段代碼提供了一個更復雜的例子:
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
]);
????}
}
例中創建了一個 5 x 10 x 15 的整型數組,并為處于 [3][5][10] 的元素賦了值為 37。注意,多維數組實際上就是數組的數組,例如,第一個 Array.get 之后,arrobj 是一個 10 x 15 的數組。進而取得其中的一個元素,即長度為 15 的數組,并使用 Array.setInt 為它的第 10 個元素賦值。
注意創建數組時的類型是動態的,在編譯時并不知道其類型。
posted on 2006-10-30 12:48
Toez 閱讀(198)
評論(0) 編輯 收藏 所屬分類:
Java