Java 的反射機制是使其具有動(dòng)態(tài)特性的非常關(guān)鍵的一種機制,也是在JavaBean 中廣泛應用的一種特性。
運用JavaBean 的最常見(jiàn)的問(wèn)題是:根據指定的類(lèi)名,類(lèi)字段名和所對應的數據,得到該類(lèi)的實(shí)例,下面的一個(gè)例子演示了這一實(shí)現。
-|Base.java //抽象基類(lèi)
|Son1.java //基類(lèi)擴展1
|Son2.java //基類(lèi)擴展2
|Util.java
/**
* @author metaphy
* create 2005-4-14 9:06:56
* 說(shuō)明:
*/
(1)Base.java 抽象基類(lèi)只是一個(gè)定義
public abstract class Base {
}
(2)Son1.java /Son2.java 是已經(jīng)實(shí)現的JavaBean
public class Son1 extends Base{
private int id ;
private String name ;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void son1Method(String s){
System.out.println(s) ;
}
}
(3)
public class Son2 extends Base{
private int id;
private double salary;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
}
(4)Util.java 演示了如何根據指定的類(lèi)名,類(lèi)字段名和所對應的數據,得到一個(gè)類(lèi)的實(shí)例
import java.lang.reflect.Method;
public class Util {
//此方法的最大好處是沒(méi)有類(lèi)名Son1,Son2 可以通過(guò)參數來(lái)指定,程序里面根本不用出現
public static Base convertStr2ServiceBean(String beanName,String fieldSetter,String paraValue){
Base base = null ;
try {
Class cls = Class.forName(beanName) ;
base = (Base)cls.newInstance() ;
Class[] paraTypes = new Class[]{String.class };
Method method = cls.getMethod(fieldSetter, paraTypes) ;
String[] paraValues = new String[]{paraValue} ;
method.invoke(base, paraValues) ;
} catch (Throwable e) {
System.err.println(e);
}
return base ;
}
public static void main(String[] args){
Son1 son1 =(Son1) Util.convertStr2ServiceBean("trying.reflect.Son1","setName","wang da sha");
System.out.println("son1.getName() :"+son1.getName()) ;
}
}
//調用結果:
//son1.getName() :wang da sha
謝謝!希望能給大家一點(diǎn)啟發(fā)!
--------------------
附:
//下面這篇文檔來(lái)源于Internet,作者不詳
Reflection 是 Java 程序開(kāi)發(fā)語(yǔ)言的特征之一,它允許運行中的 Java 程序對自身進(jìn)行檢查,或者說(shuō)“自審”,并能直接操作程序的內部屬性。例如,使用它能獲得 Java 類(lèi)中各成員的名稱(chēng)并顯示出來(lái)。
Java 的這一能力在實(shí)際應用中也許用得不是很多,但是在其它的程序設計語(yǔ)言中根本就不存在這一特性。例如,Pascal、C 或者 C++ 中就沒(méi)有辦法在程序中獲得函數定義相關(guān)的信息。
JavaBean 是 reflection 的實(shí)際應用之一,它能讓一些工具可視化的操作軟件組件。這些工具通過(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ǔ)句執行:
java DumpMethods java.util.Stack
它的結果輸出為:
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),然后調用 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 對象。在運行中的 Java 程序中,用 java.lang.Class 類(lèi)來(lái)描述類(lèi)和接口等。
下面就是獲得一個(gè) Class 對象的方法之一:
Class c = Class.forName("java.lang.String");
這條語(yǔ)句得到一個(gè) String 類(lèi)的類(lèi)對象。還有另一種方法,如下面的語(yǔ)句:
Class c = int.class;
或者
Class c = Integer.TYPE;
它們可獲得基本類(lèi)型的類(lèi)信息。其中后一種方法中訪(fǎng)問(wèn)的是基本類(lèi)型的封裝類(lèi) (如 Integer) 中預先定義好的 TYPE 字段。
第二步是調用諸如 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 處理特殊應用程序提供例證。
模擬 instanceof 操作符
得到類(lèi)信息之后,通常下一個(gè)步驟就是解決關(guān)于 Class 對象的一些基本的問(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 對象,然后檢查一些對象是否是 A 的實(shí)例。Integer(37) 不是,但 new A() 是。
3.找出類(lèi)的方法
找出一個(gè)類(lèi)中定義了些什么方法,這是一個(gè)非常有價(jià)值也非?;A的 reflection 用法。下面的代碼就實(shí)現了這一用法:
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)的描述,然后調用 getDeclaredMethods 來(lái)獲取一系列的 Method 對象,它們分別描述了定義在類(lèi)中的每一個(gè)方法,包括 public 方法、protected 方法、package 方法和 private 方法等。如果你在程序中使用 getMethods 來(lái)代替 getDeclaredMethods,你還能獲得繼承來(lái)的各個(gè)方法的信息。
取得了 Method 對象列表之后,要顯示這些方法的參數類(lèi)型、異常類(lèi)型和返回值類(lèi)型等就不難了。這些類(lèi)型是基本類(lèi)型還是類(lèi)類(lèi)型,都可以由描述類(lèi)的對象按順序給出。
輸出的結果如下:
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.獲取構造器信息
獲取類(lèi)構造器的用法與上述獲取方法的用法類(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)信息,那是因為構造器沒(méi)有返回類(lèi)型。
這個(gè)程序運行的結果是:
name = constructor1
decl class = class constructor1
-----
name = constructor1
decl class = class constructor1
param #0 int
param #1 double
-----
5.獲取類(lèi)的字段(域)
找出一個(gè)類(lèi)中定義了哪些數據字段也是可能的,下面的代碼就在干這個(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ǔ)自身由整數描述,而且使用 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í)候也可以只取得在當前類(lèi)中申明了的字段信息 (getDeclaredFields),或者也可以取得父類(lèi)中定義的字段 (getFields) 。
6.根據方法的名稱(chēng)來(lái)執行方法
文本到這里,所舉的例子無(wú)一例外都與如何獲取類(lèi)的信息有關(guān)。我們也可以用 reflection 來(lái)做一些其它的事情,比如執行一個(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è)程序在執行的某處的時(shí)候才知道需要執行某個(gè)方法,這個(gè)方法的名稱(chēng)是在程序的運行過(guò)程中指定的 (例如,JavaBean 開(kāi)發(fā)環(huán)境中就會(huì )做這樣的事),那么上面的程序演示了如何做到。
上例中,getMethod 用于查找一個(gè)具有兩個(gè)整型參數且名為 add 的方法。找到該方法并創(chuàng )建了相應的 Method 對象之后,在正確的對象實(shí)例中執行它。執行該方法的時(shí)候,需要提供一個(gè)參數列表,這在上例中是分別包裝了整數 37 和 47 的兩個(gè) Integer 對象。執行方法的返回的同樣是一個(gè) Integer 對象,它封裝了返回值 84。
7.創(chuàng )建新的對象
對于構造器,則不能像執行方法那樣進(jìn)行,因為執行一個(gè)構造器就意味著(zhù)創(chuàng )建了一個(gè)新的對象 (準確的說(shuō),創(chuàng )建一個(gè)對象的過(guò)程包括分配內存和構造對象)。所以,與上例最相似的例子如下:
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);
}
}
}
根據指定的參數類(lèi)型找到相應的構造函數并執行它,以創(chuàng )建一個(gè)新的對象實(shí)例。使用這種方法可以在程序運行時(shí)動(dòng)態(tài)地創(chuàng )建對象,而不是在編譯的時(shí)候創(chuàng )建對象,這一點(diǎn)非常有價(jià)值。
8.改變字段(域)的值
reflection 的還有一個(gè)用處就是改變對象數據字段的值。reflection 可以從正在運行的程序中根據名稱(chēng)找到對象的字段并改變它,下面的例子可以說(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.使用數組
本文介紹的 reflection 的最后一種用法是創(chuàng )建的操作數組。數組在 Java 語(yǔ)言中是一種特殊的類(lèi)類(lèi)型,一個(gè)數組的引用可以賦給 Object 引用。觀(guān)察下面的例子看看數組是怎么工作的:
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è)單位長(cháng)度的 String 數組,為第 5 個(gè)位置的字符串賦了值,最后將這個(gè)字符串從數組中取得并打印了出來(lái)。
下面這段代碼提供了一個(gè)更復雜的例子:
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 的整型數組,并為處于 [3][5][10] 的元素賦了值為 37。注意,多維數組實(shí)際上就是數組的數組,例如,第一個(gè) Array.get 之后,arrobj 是一個(gè) 10 x 15 的數組。進(jìn)而取得其中的一個(gè)元素,即長(cháng)度為 15 的數組,并使用 Array.setInt 為它的第 10 個(gè)元素賦值。
注意創(chuàng )建數組時(shí)的類(lèi)型是動(dòng)態(tài)的,在編譯時(shí)并不知道其類(lèi)型。