一:概述
Java中,反射是一種強大的工具。它使您可以建立靈活的代碼,這些代碼能夠在運行時裝配,無需在組件之間進行源表明連接。反射容許咱們在編寫與執行時,使咱們的程序代碼可以接入裝載到JVM中的類的內部信息,而不是源代碼中選定的類協做的代碼。這使反射成爲構建靈活的應用的主要工具。但需注意的是:若是使用不當,反射的成本很高。
Reflection 是 Java 程序開發語言的特徵之一,它容許運行中的 Java 程序對自身進行檢查,或者說「自審」,並能直接操做程序的內部屬性。Java 的這一能力在實際應用中也許用得不是不少,可是在其它的程序設計語言中根本就不存在這一特性。例如,Pascal、C 或者 C++ 中就沒有辦法在程序中得到函數定義相關的信息。
1.檢測類:
1.1 reflection的工做機制
考慮下面這個簡單的例子,讓咱們看看 reflection 是如何工做的。 java
按以下語句執行:
它的結果輸出爲:
public void javax.swing.JFrame.remove(java.awt.Component)
public void javax.swing.JFrame.update(java.awt.Graphics)
public javax.accessibility.AccessibleContext javax.swing.JFrame.getAccessibleContext()
protected java.lang.String javax.swing.JFrame.paramString()
protected void javax.swing.JFrame.addImpl(java.awt.Component,java.lang.Object,int)
public void javax.swing.JFrame.setLayout(java.awt.LayoutManager)
protected void javax.swing.JFrame.processWindowEvent(java.awt.event.WindowEvent)
public void javax.swing.JFrame.setIconImage(java.awt.Image)
public java.awt.Container javax.swing.JFrame.getContentPane()
public java.awt.Component javax.swing.JFrame.getGlassPane()
public javax.swing.JLayeredPane javax.swing.JFrame.getLayeredPane()
public javax.swing.JRootPane javax.swing.JFrame.getRootPane()
public void javax.swing.JFrame.setContentPane(java.awt.Container)
public void javax.swing.JFrame.setGlassPane(java.awt.Component)
public void javax.swing.JFrame.setLayeredPane(javax.swing.JLayeredPane)
protected javax.swing.JRootPane javax.swing.JFrame.createRootPane()
protected void javax.swing.JFrame.frameInit()
public int javax.swing.JFrame.getDefaultCloseOperation()
public javax.swing.JMenuBar javax.swing.JFrame.getJMenuBar()
public static boolean javax.swing.JFrame.isDefaultLookAndFeelDecorated()
protected boolean javax.swing.JFrame.isRootPaneCheckingEnabled()
public void javax.swing.JFrame.setDefaultCloseOperation(int)
public static void javax.swing.JFrame.setDefaultLookAndFeelDecorated(boolean)
public void javax.swing.JFrame.setJMenuBar(javax.swing.JMenuBar)
protected void javax.swing.JFrame.setRootPane(javax.swing.JRootPane)
protected void javax.swing.JFrame.setRootPaneCheckingEnabled(boolean)
這樣就列出了javax.swing.JFrame類的各方法名以及它們的限制符和返回類型。
這個程序使用 Class.forName 載入指定的類,而後調用 getDeclaredMethods 來獲取這個類中定義了的方法列表。java.lang.reflect.Methods 是用來描述某個類中單個方法的一個類。
1.2 Java類反射中的主要方法
對於如下三類組件中的任何一類來講 -- 構造函數、字段和方法 -- java.lang.Class 提供四種獨立的反射調用,以不一樣的方式來得到信息。調用都遵循一種標準格式。如下是用於查找構造函數的一組反射調用:
Constructor getConstructor(Class[] params) -- 得到使用特殊的參數類型的公共構造函數,
Constructor[] getConstructors() -- 得到類的全部公共構造函數
Constructor getDeclaredConstructor(Class[] params) -- 得到使用特定參數類型的構造函數(與接入級別無關)
Constructor[] getDeclaredConstructors() -- 得到類的全部構造函數(與接入級別無關)
得到字段信息的Class 反射調用不一樣於那些用於接入構造函數的調用,在參數類型數組中使用了字段名:
Field getField(String name) -- 得到命名的公共字段
Field[] getFields() -- 得到類的全部公共字段
Field getDeclaredField(String name) -- 得到類聲明的命名的字段
Field[] getDeclaredFields() -- 得到類聲明的全部字段
用於得到方法信息函數:
Method getMethod(String name, Class[] params) -- 使用特定的參數類型,得到命名的公共方法
Method[] getMethods() -- 得到類的全部公共方法
Method getDeclaredMethod(String name, Class[] params) -- 使用特寫的參數類型,得到類聲明的命名的方法
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是指實例).
例如: 編程
2、安全性和反射
在處理反射時安全性是一個較複雜的問題。反射常常由框架型代碼使用,因爲這一點,咱們可能但願框架可以全面接入代碼,無需考慮常規的接入限制。可是,在其它狀況下,不受控制的接入會帶來嚴重的安全性風險,例如當代碼在不值得信任的代碼共享的環境中運行時。
因爲這些互相矛盾的需求,Java編程語言定義一種多級別方法來處理反射的安全性。基本模式是對反射實施與應用於源代碼接入相同的限制:
從任意位置到類公共組件的接入
類自身外部無任何到私有組件的接入
受保護和打包(缺省接入)組件的有限接入
不過至少有些時候,圍繞這些限制還有一種簡單的方法。咱們能夠在咱們所寫的類中,擴展一個普通的基本類java.lang.reflect.AccessibleObject 類。這個類定義了一種setAccessible方法,使咱們可以啓動或關閉對這些類中其中一個類的實例的接入檢測。惟一的問題在於若是使用了安全性管理器,它將檢測正在關閉接入檢測的代碼是否許可了這樣作。若是未許可,安全性管理器拋出一個例外。
3、Java反射有兩個缺點
第一:性能問題。用於字段和方法接入時反射要遠慢於直接代碼。性能問題的程度取決於程序中是如何使用反射的。若是它做爲程序運行中相對不多涉及的部分,緩慢的性能將不會是一個問題。即便測試中最壞狀況下的計時圖顯示的反射操做只耗用幾微秒。僅反射在性能關鍵的應用的核心邏輯中使用時性能問題才變得相當重要。
第二:許多應用中更嚴重的一個缺點是使用反射會模糊程序內部實際要發生的事情。程序人員但願在源代碼中看到程序的邏輯,反射等繞過了源代碼的技術會帶來維護問題。反射代碼比相應的直接代碼更復雜,正如性能比較的代碼實例中看到的同樣。解決這些問題的最佳方案是保守地使用反射——僅在它能夠真正增長靈活性的地方——記錄其在目標類中的使用。
完整實例代碼: 數組