反射

反射的概念是由Smith在1982年首次提出的,主要是指程序能夠訪問、檢測和修改它自己狀態或行爲的一種能力。這一律唸的提出很快引起了計算機科學領域關於應用反射性的研究。它首先被程序語言的設計領域所採用,並在Lisp和麪向對象方面取得了成績。其中LEAD/LEAD++ 、OpenC++ 、MetaXa和OpenJava等就是基於反射機制的語言。最近,反射機制也被應用到了視窗系統、操做系統和文件系統中。

反射自己並非一個新概念,它可能會使咱們聯想到光學中的反射概念,儘管計算機科學賦予了反射概念新的含義,可是,從現象上來講,它們確實有某些相通之處,這些有助於咱們的理解。在計算機科學領域,反射是指一類應用,它們可以自描述和自控制。也就是說,這類應用經過採用某種機制來實現對本身行爲的描述(self-representation)和監測(examination),並能根據自身行爲的狀態和結果,調整或修改應用所描述行爲的狀態和相關的語義。能夠看出,同通常的反射概念相比,計算機科學領域的反射不僅僅指反射自己,還包括對反射結果所採起的措施。全部採用反射機制的系統(即反射系統)都但願使系統的實現更開放。能夠說,實現了反射機制的系統都具備開放性,但具備開放性的系統並不必定採用了反射機制,開放性是反射系統的必要條件。通常來講,反射系統除了知足開放性條件外還必須知足緣由鏈接(Causally-connected)。所謂緣由鏈接是指對反射系統自描述的改變可以當即反映到系統底層的實際狀態和行爲上的狀況,反之亦然。開放性和緣由鏈接是反射系統的兩大基本要素。

Java中,反射是一種強大的工具。它使您可以建立靈活的代碼,這些代碼能夠在運行時裝配,無需在組件之間進行源表明連接。反射容許咱們在編寫與執行時,使咱們的程序代碼可以接入裝載到JVM中的類的內部信息,而不是源代碼中選定的類協做的代碼。這使反射成爲構建靈活的應用的主要工具。但需注意的是:若是使用不當,反射的成本很高。

2、Java中的類反射:
Reflection 是 Java 程序開發語言的特徵之一,它容許運行中的 Java 程序對自身進行檢查,或者說「自審」,並能直接操做程序的內部屬性。Java 的這一能力在實際應用中也許用得不是不少,可是在其它的程序設計語言中根本就不存在這一特性。例如,Pascal、C 或者 C++ 中就沒有辦法在程序中得到函數定義相關的信息。

1.檢測類:

1.1 reflection的工做機制

考慮下面這個簡單的例子,讓咱們看看 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);
}
}
}

按以下語句執行:

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 類的各方法名以及它們的限制符和返回類型。

這個程序使用 Class.forName 載入指定的類,而後調用 getDeclaredMethods 來獲取這個類中定義了的方法列表。java.lang.reflect.Methods 是用來描述某個類中單個方法的一個類。

1.2 Java類反射中的主要方法

對於如下三類組件中的任何一類來講 -- 構造函數、字段和方法 -- java.lang.Class 提供四種獨立的反射調用,以不一樣的方式來得到信息。調用都遵循一種標準格式。如下是用於查找構造函數的一組反射調用:

l Constructor getConstructor(Class[] params) -- 得到使用特殊的參數類型的公共構造函數,

l Constructor[] getConstructors() -- 得到類的全部公共構造函數

l Constructor getDeclaredConstructor(Class[] params) -- 得到使用特定參數類型的構造函數(與接入級別無關)

l Constructor[] getDeclaredConstructors() -- 得到類的全部構造函數(與接入級別無關)

得到字段信息的Class 反射調用不一樣於那些用於接入構造函數的調用,在參數類型數組中使用了字段名:

l Field getField(String name) -- 得到命名的公共字段

l Field[] getFields() -- 得到類的全部公共字段

l Field getDeclaredField(String name) -- 得到類聲明的命名的字段

l Field[] getDeclaredFields() -- 得到類聲明的全部字段

用於得到方法信息函數:

l Method getMethod(String name, Class[] params) -- 使用特定的參數類型,得到命名的公共方法

l Method[] getMethods() -- 得到類的全部公共方法

l Method getDeclaredMethod(String name, Class[] params) -- 使用特寫的參數類型,得到類聲明的命名的方法

l Method[] getDeclaredMethods() -- 得到類聲明的全部方法



1.3開始使用 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 中定義的第一個方法的原型。

2.處理對象:

若是要做一個開發工具像debugger之類的,你必須能發現filed values,如下是三個步驟:

a.建立一個Class對象
b.經過getField 建立一個Field對象
c.調用Field.getXXX(Object)方法(XXX是Int,Float等,若是是對象就省略;Object是指實例).

例如:
import java.lang.reflect.*;
import java.awt.*;

class SampleGet {

public static void main(String[] args) {
Rectangle r = new Rectangle(100, 325);
printHeight(r);

}

static void printHeight(Rectangle r) {
Field heightField;
Integer heightValue;
Class c = r.getClass();
try {
heightField = c.getField("height");
heightValue = (Integer) heightField.get(r);
System.out.println("Height: " + heightValue.toString());
} catch (NoSuchFieldException e) {
System.out.println(e);
} catch (SecurityException e) {
System.out.println(e);
} catch (IllegalAccessException e) {
System.out.println(e);
}
}
}

3、安全性和反射:
在處理反射時安全性是一個較複雜的問題。反射常常由框架型代碼使用,因爲這一點,咱們可能但願框架可以全面接入代碼,無需考慮常規的接入限制。可是,在其它狀況下,不受控制的接入會帶來嚴重的安全性風險,例如當代碼在不值得信任的代碼共享的環境中運行時。

因爲這些互相矛盾的需求,Java編程語言定義一種多級別方法來處理反射的安全性。基本模式是對反射實施與應用於源代碼接入相同的限制:

n 從任意位置到類公共組件的接入

n 類自身外部無任何到私有組件的接入

n 受保護和打包(缺省接入)組件的有限接入

不過至少有些時候,圍繞這些限制還有一種簡單的方法。咱們能夠在咱們所寫的類中,擴展一個普通的基本類java.lang.reflect.AccessibleObject 類。這個類定義了一種setAccessible方法,使咱們可以啓動或關閉對這些類中其中一個類的實例的接入檢測。惟一的問題在於若是使用了安全性管理器,它將檢測正在關閉接入檢測的代碼是否許可了這樣作。若是未許可,安全性管理器拋出一個例外。

下面是一段程序,在TwoString 類的一個實例上使用反射來顯示安全性正在運行:

public class ReflectSecurity {

public static void main(String[] args) {

try {

TwoString ts = new TwoString("a", "b");

Field field = clas.getDeclaredField("m_s1");

// field.setAccessible(true);

System.out.println("Retrieved value is " +

field.get(inst));

} catch (Exception ex) {

ex.printStackTrace(System.out);

}

}

}

若是咱們編譯這一程序時,不使用任何特定參數直接從命令行運行,它將在field .get(inst)調用中拋出一個IllegalAccessException異常。若是咱們不註釋field.setAccessible(true)代碼行,那麼從新編譯並從新運行該代碼,它將編譯成功。最後,若是咱們在命令行添加了JVM參數-Djava.security.manager以實現安全性管理器,它仍然將不能經過編譯,除非咱們定義了ReflectSecurity類的許可權限。

4、反射性能:
反射是一種強大的工具,但也存在一些不足。一個主要的缺點是對性能有影響。使用反射基本上是一種解釋操做,咱們能夠告訴JVM,咱們但願作什麼而且它知足咱們的要求。這類操做老是慢於只直接執行相同的操做。

下面的程序是字段接入性能測試的一個例子,包括基本的測試方法。每種方法測試字段接入的一種形式 -- accessSame 與同一對象的成員字段協做,accessOther 使用可直接接入的另外一對象的字段,accessReflection 使用可經過反射接入的另外一對象的字段。在每種狀況下,方法執行相同的計算 -- 循環中簡單的加/乘順序。

程序以下:

public int accessSame(int loops) {

m_value = 0;

for (int index = 0; index < loops; index++) {

m_value = (m_value + ADDITIVE_VALUE) *

MULTIPLIER_VALUE;

}

return m_value;

}



public int accessReference(int loops) {

TimingClass timing = new TimingClass();

for (int index = 0; index < loops; index++) {

timing.m_value = (timing.m_value + ADDITIVE_VALUE) *

MULTIPLIER_VALUE;

}

return timing.m_value;

}



public int accessReflection(int loops) throws Exception {

TimingClass timing = new TimingClass();

try {

Field field = TimingClass.class.

getDeclaredField("m_value");

for (int index = 0; index < loops; index++) {

int value = (field.getInt(timing) +

ADDITIVE_VALUE) * MULTIPLIER_VALUE;

field.setInt(timing, value);

}

return timing.m_value;

} catch (Exception ex) {

System.out.println("Error using reflection");

throw ex;

}

}

在上面的例子中,測試程序重複調用每種方法,使用一個大循環數,從而平均屢次調用的時間衡量結果。平均值中不包括每種方法第一次調用的時間,所以初始化時間不是結果中的一個因素。下面的圖清楚的向咱們展現了每種方法字段接入的時間:

圖 1:字段接入時間 :


咱們能夠看出:在前兩副圖中(Sun JVM),使用反射的執行時間超過使用直接接入的1000倍以上。經過比較,IBM JVM可能稍好一些,但反射方法仍舊須要比其它方法長700倍以上的時間。任何JVM上其它兩種方法之間時間方面無任何顯著差別,但IBM JVM幾乎比Sun JVM快一倍。最有可能的是這種差別反映了Sun Hot Spot JVM的專業優化,它在簡單基準方面表現得很糟糕。反射性能是Sun開發1.4 JVM時關注的一個方面,它在反射方法調用結果中顯示。在這類操做的性能方面,Sun 1.4.1 JVM顯示了比1.3.1版本很大的改進。

若是爲爲建立使用反射的對象編寫了相似的計時測試程序,咱們會發現這種狀況下的差別不象字段和方法調用狀況下那麼顯著。使用newInstance()調用建立一個簡單的java.lang.Object實例耗用的時間大約是在Sun 1.3.1 JVM上使用new Object()的12倍,是在IBM 1.4.0 JVM的四倍,只是Sun 1.4.1 JVM上的兩部。使用Array.newInstance(type, size)建立一個數組耗用的時間是任何測試的JVM上使用new type[size]的兩倍,隨着數組大小的增長,差別逐步縮小。

結束語:
Java語言反射提供一種動態連接程序組件的多功能方法。它容許程序建立和控制任何類的對象(根據安全性限制),無需提早硬編碼目標類。這些特性使得反射特別適用於建立以很是普通的方式與對象協做的庫。例如,反射常常在持續存儲對象爲數據庫、XML或其它外部格式的框架中使用。Java reflection 很是有用,它使類和數據結構能按名稱動態檢索相關信息,並容許在運行着的程序中操做這些信息。Java 的這一特性很是強大,而且是其它一些經常使用語言,如 C、C++、Fortran 或者 Pascal 等都不具有的。

但反射有兩個缺點。第一個是性能問題。用於字段和方法接入時反射要遠慢於直接代碼。性能問題的程度取決於程序中是如何使用反射的。若是它做爲程序運行中相對不多涉及的部分,緩慢的性能將不會是一個問題。即便測試中最壞狀況下的計時圖顯示的反射操做只耗用幾微秒。僅反射在性能關鍵的應用的核心邏輯中使用時性能問題才變得相當重要。

許多應用中更嚴重的一個缺點是使用反射會模糊程序內部實際要發生的事情。程序人員但願在源代碼中看到程序的邏輯,反射等繞過了源代碼的技術會帶來維護問題。反射代碼比相應的直接代碼更復雜,正如性能比較的代碼實例中看到的同樣。解決這些問題的最佳方案是保守地使用反射——僅在它能夠真正增長靈活性的地方——記錄其在目標類中的使用。

最近在成都寫一個移動增值項目,俺負責後臺server端。功能很簡單,手機用戶經過GPRS打開Socket與服務器鏈接,我則根據用戶傳過來的數據作出響應。作過相似項目的兄弟必定都知道,首先須要定義一個相似於MSNP的通信協議,不過今天的話題是如何把這個系統設計得具備高度的擴展性。因爲這個項目自己沒有進行過較爲完善的客戶溝通和需求分析,因此之後確定會有不少功能上的擴展,通信協議確定會愈來愈龐大,而我做爲一個不那麼勤快的人,固然不想之後再去修改寫好的程序,因此這個項目是實踐面向對象設計的好機會。

首先定義一個接口來隔離類:

package org.bromon.reflect;

public interface Operator

{

public java.util.List act(java.util.List params)

}

根據設計模式的原理,咱們能夠爲不一樣的功能編寫不一樣的類,每一個類都繼承Operator接口,客戶端只須要針對Operator接口編程就能夠避免不少麻煩。好比這個類:

package org.bromon.reflect.*;

public class Success implements Operator

{

public java.util.List act(java.util.List params)

{

List result=new ArrayList();java

 

 

 

Java 的反射機制是使其具備動態特性的很是關鍵的一種機制,也是在JavaBean 中普遍應用的一種特性。
運用JavaBean 的最多見的問題是:根據指定的類名,類字段名和所對應的數據,獲得該類的實例,下面的一個例子演示了這一實現。
-|Base.java //抽象基類
|Son1.java //基類擴展1
|Son2.java //基類擴展2
|Util.java
/**
* @author metaphy
* create 2005-4-14 9:06:56
* 說明:
*/
(1)Base.java 抽象基類只是一個定義
public abstract class Base {
}
(2)Son1.java /Son2.java 是已經實現的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 演示瞭如何根據指定的類名,類字段名和所對應的數據,獲得一個類的實例
import java.lang.reflect.Method;
public class Util {
//此方法的最大好處是沒有類名Son1,Son2 能夠經過參數來指定,程序裏面根本不用出現
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

謝謝!但願能給你們一點啓發!
--------------------
附:
//下面這篇文檔來源於Internet,做者不詳
Reflection 是 Java 程序開發語言的特徵之一,它容許運行中的 Java 程序對自身進行檢查,或者說「自審」,並能直接操做程序的內部屬性。例如,使用它能得到 Java 類中各成員的名稱並顯示出來。
Java 的這一能力在實際應用中也許用得不是不少,可是在其它的程序設計語言中根本就不存在這一特性。例如,Pascal、C 或者 C++ 中就沒有辦法在程序中得到函數定義相關的信息。
JavaBean 是 reflection 的實際應用之一,它能讓一些工具可視化的操做軟件組件。這些工具經過 reflection 動態的載入並取得 Java 組件(類) 的屬性。
1. 一個簡單的例子
考慮下面這個簡單的例子,讓咱們看看 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);
}
}
}
按以下語句執行:
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 類的各方法名以及它們的限制符和返回類型。
這個程序使用 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 個元素賦值。
注意建立數組時的類型是動態的,在編譯時並不知道其類型。數據庫

相關文章
相關標籤/搜索