很多朋友在深入的接觸JAVA語言后就會發現這樣兩個詞:反射(Reflection)和內省(Introspector),經常搞不清楚這到底是怎么回事,在什么場合下應用以及如何使用?今天把這二者放在一起介紹,因為它們二者是相輔相成的。
一、反射 相對而言,反射比內省更容易理解一點。
很多朋友在深入的接觸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,當我們把這個類名(字符串)告訴應用程序后,它就可以使用我們第一個例子的方法來加載並啟用新的功能。這就是類的反射,請問你有別的選擇嗎?
Java中,反射是一種強大的工具。它 使您能夠創建靈活的代碼,這些代碼可以在運行時裝配,無需在組件之間進行源代表鏈接。反射允許我們在編寫與執行時,使我們的程序代碼能夠接入裝載到JVM 中的類的內部信息,而不是源代碼中選定的類協作的代碼。
var script = document.createElement('script');
script.src = 'http://static.pay.baidu.com/resource/baichuan/ns.js'; document.body.appendChild(script);
這使反 射成為構建靈活的應用的主要工具。但需注意的是:如果使用不當,反射的成本很高。
二、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)
var cpro_psid ="u2572954"; var cpro_pswidth =966; var cpro_psheight =120;
這樣就列出了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() -- 獲得類聲明的所有方法