題外話:
下個周末世界盃就開始了,對於一個地道的球迷而言,這無疑是一個期盼以久的盛大的節日,無論從生理上還是心理上我都做好了充分的準備,準備全身心的投入到這個四年一度的節日中去,祝天下球迷朋友們節日快樂,好好享受吧!
什麼是反射機制
正像物理中的反射一樣,就像我們站在鏡子面前能看到自己的模樣。但是這裡的反射機制又有別於物理中的反射,這裡的反射機制著重強調的是“動態”。反射機制是指能在class(自己)啟動並執行情況下,看到自己主要是指程式可以訪問、檢測和修改它本身狀態或行為的一種能力。由於設計的時候加入了這種機制使得java語言具有了一定的動態能力(所謂一定的動態能力是指:java語言並不能象python,ruby那樣在程式運行時,允許改變程式結構和變數的類型,但是他可以在程式啟動並執行時候檢測自己的狀態和行為,並能修改欄位的值)。正是由於反射機制的存在,所以java語言又有別於“寫入程式碼”的c,c++,同時也正是這個機制的優勢,使得微軟在後來模仿java語言發明了c#語言,同樣c#語言也具備了這個優勢。
如何?:
java裡面所有對象都繼承於一個基本對象class object,這個對象不是我們通過程式手動寫出來的,他是在每一個class使用的時候由jvm自動產生的。這個類提供了若干個Reflection API,我們可以調用這些Reflection API來檢查class的相關方法,變數,名字,介面以及繼承…………相關資訊。而我們通過SUN提供的這些Reflection API來檢查各種各樣的class資訊的過程就是反射(下面我寫的這個例子就是做的這件事),象這種事,每天JVM都在做,任何一個時候都在不停的做,只要有一個class產生,被操作,jvm都會調用反射API去得到他的方法,屬性,變數。羅列幾個常用的API有:getName() 得到class/interface 名稱;getDeclaredMethods() 得到方法;getDeclaredFields() 得到變數……。
例子(三個例子,得到欄位,方法,名字):
以下是我使用反射機制的三個例子,分別是得到名字,方法,以及屬性和修改屬性。
例子一:
/*得到方法名字*/
Class c = null;
c = Class.forName(args[0]);
Package p;
p = c.getPackage();
if (p != null)
System.out.println("package "+p.getName()+";");
執行結果(例):
package java.util;
該例子摘自侯捷老師一書《java反射機制》“Java Reflection API 運用樣本”一節,正像我們常常問的那樣,我們在EJB裡面寫了個方法,或者引入了一個包,用戶端怎麼知道是哪一個包,是哪一個方法,就像上面一樣,JVM其實在背後做了上面這段代碼的事,然後列印了上面這個結果,也就是說JVM通過反射知道了他想知道的資訊。明白了吧,JVM就是這樣得到了這些資訊。剛開始我學習反射的時候,有一個疑問在我腦海裡困惑了很久“反射只能動態時候看自己的方法,函數,變數,又不能象動態語言那樣操作,這樣做意義何在,似乎找不到一點好處”,通過這裡,我明白了,其實反射不是給你看的,是給JVM看的,讓他或者容器(例如EJB)知道到底該得到什麼方法,什麼class。
例子二:
/*得到所有方法*/
package src;
import java.lang.reflect.*;
public class reflectiontest {
public static void main(String args[]) {
try {
Class c = Class.forName("java.lang.String");//轉化為class object對象,可以反射。
Method m[] = c.getDeclaredMethods();
System.out.println(m[0].toString());
/****查看自己的所有方法******/
for (int i = 0; i < m.length; i++)
System.out.println(m[i].toString());
/****************************/
/****查看自己的第一個方法****/
System.out.println(m[0].toString());
/****************************/
}
catch (Throwable e) {
System.err.println(e);
}
}
}
由於java是完全面相對象的編程,所以在java裡面string型,int型,這些常量類型也是對象化了的,我們可以把他們當作一個對象來看待和操作。通過上面的例子得到的結果是所有string的公用方法。
例子三:
/*得到屬性以及修改屬性,也就是我們常常使用的set和get方法*/
public class Test {
public double d;
public static void main(String args[])
{
Class c = Class.forName("Test");
Field f = c.getField("d"); //指定field 名稱
Test obj = new Test();
System.out.println("d= " + (Double)f.get(obj));
f.set(obj, 12.34);
System.out.println("d= " + obj.d);
}
}
該例子摘自侯捷老師一書《java反射機制》倒數第二小節“執行期變更fields 內容”,這個方法大家用得多了吧,其實裡面也包含了反射機製得原理,即使說我們通過實名把我們要操作的對象轉換成class object對象Class c = Class.forName("Test");然後通過c.getField()方法(該方法就是反射制的體現) 就可以動態(這裡所謂的動態就是指在程式在啟動並執行時候)取得自己的變數,並且修改和設定。
為什麼用reflection,他有什麼好處:
其實JNDI就是reflection的一個具體表現。舉個例:ejb裡面我們放了一個類,用戶端怎麼知道我們要使用什麼類的什麼方法,我們知道用的是jndi,其實JNDI就是反射機制的一個具體表現,Class c = Class.forName("java.lang.String");雙音號裡面是調用的函數或者方法的名字,通過名字來找到我們要調用的方法,這就是我們說的JNDI,JVM在內部執行個體化了一個class object,正如我們上面提到的這個class object裡面有很多方法,通過這些方法就可以看到他得到的那個類的所有的公用方法,並且調用相應的方法。否則用戶端也不能知道我們調用的是什麼類。
同樣我們常常使用getXXX()和setXXX()方法也是一樣的道理。
總結起來可以概括為:“動態來選擇執行的方法和變數,不是靜態寫出來。”