轉自這裡
Java的反射機制是Java特性之一,反射機制是構建架構技術的基礎所在。靈活掌握Java反射機制,對大家以後學習架構技術有很大的協助。
那麼什麼是Java的反射呢?
大家都知道,要讓Java程式能夠運行,那麼就得讓Java類要被Java虛擬機器載入。Java類如果不被Java虛擬機器載入,是不能正常啟動並執行。現在我們啟動並執行所有的程式都是在編譯期的時候就已經知道了你所需要的那個類的已經被載入了。
Java的反射機制是在編譯並不確定是哪個類被載入了,而是在程式啟動並執行時候才載入、探知、自審。使用在編譯期並不知道的類。這樣的特點就是反射。
那麼Java反射有什麼作用呢?
假如我們有兩個程式員,一個程式員在寫程式的時候,需要使用第二個程式員所寫的類,但第二個程式員並沒完成他所寫的類。那麼第一個程式員的代碼能否通過編譯呢?這是不能通過編譯的。利用Java反射的機制,就可以讓第一個程式員在沒有得到第二個程式員所寫的類的時候,來完成自身代碼的編譯。
Java的反射機制它知道類的基本結構,這種對Java類結構探知的能力,我們稱為Java類的“自審”。大家都用過Jcreator和eclipse。當我們構建出一個對象的時候,去調用該對象的方法和屬性的時候。一按點,編譯工具就會自動的把該對象能夠使用的所有的方法和屬性全部都列出來,供使用者進行選擇。這就是利用了Java反射的原理,是對我們建立對象的探知、自審。
Class類
要正確使用Java反射機制就得使用java.lang.Class這個類。它是Java反射機制的起源。當一個類被載入以後,Java虛擬機器就會自動產生一個Class對象。通過這個Class對象我們就能獲得載入到虛擬機器當中這個Class對象對應的方法、成員以及構造方法的聲明和定義等資訊。
反射API
u反射API用於反應在當前Java虛擬機器中的類、介面或者對象資訊
u功能
—擷取一個對象的類資訊.
—擷取一個類的存取修飾詞、成員、方法、構造方法以及超類的資訊.
—檢獲屬於一個介面的常量和方法聲明.
—建立一個直到程式運行期間才知道名字的類的執行個體.
—擷取並設定一個對象的成員,甚至這個成員的名字是
在程式運行期間才知道.
—檢測一個在運行期間才知道名字的對象的方法
利用Java反射機制我們可以很靈活的對已經載入到Java虛擬機器當中的類資訊進行檢測。當然這種檢測在對啟動並執行效能上會有些減弱,所以什麼時候使用反射,就要靠業務的需求、大小,以及經驗的積累來決定。
那麼如何利用反射API在啟動並執行時候知道一個類的資訊呢?
程式碼範例:
import java.lang.reflect.Field;import java.lang.reflect.Method;import javax.swing.JOptionPane;/** *本類用於測試反射API,利用使用者輸入類的全路徑,*找到該類所有的成員方法和成員屬性 */public class MyTest { /** *構造方法 */ public MyTest(){ String classInfo=JOptionPane.showInputDialog(null,"輸入類全路徑");//要求使用者輸入類的全路徑 try { Class cla=Class.forName(classInfo);//根據類的全路徑進行類載入,返回該類的Class對象 Method[] method=cla.getDeclaredMethods();//利用得到的Class對象的自審,返回方法對象集合 for(Method me:method){//遍曆該類方法的集合 System.out.println(me.toString());//列印方法資訊 } System.out.println("********"); Field[] field=cla.getDeclaredFields();//利用得到的Class對象的自審,返回屬性對象集合 for(Field me:field){ //遍曆該類屬性的集合 System.out.println(me.toString());//列印屬性資訊 } } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static void main(String[] args) { new MyTest(); }}
啟動並執行時候,我們輸入javax.swing.JFrame,那麼運行結果如下:
public void javax.swing.JFrame.remove(java.awt.Component)
public void javax.swing.JFrame.update(java.awt.Graphics)
…………
********
public static final int javax.swing.JFrame.EXIT_ON_CLOSE
private int javax.swing.JFrame.defaultCloseOperation
…………
大家可以發現,類的全路徑是在程式啟動並執行時候,由使用者輸入的。所以虛擬機器事先並不知道所要載入類的資訊,這就是利用反射機制來對使用者輸入的類全路徑來對類自身的一個自審。從而探知該類所擁有的方法和屬性。
通過上面代碼,大家可以知道編譯工具為什麼能夠一按點就能列出使用者當前對象的屬性和方法了。它是先獲得使用者輸入對象的字串,然後利用反射原理來對這樣的類進行自審,從而列出該類的方法和屬性。
使用反射機制的步驟:
u匯入java.lang.relfect 包
u遵循三個步驟
第一步是獲得你想操作的類的 java.lang.Class 對象
第二步是調用諸如 getDeclaredMethods 的方法
第三步使用 反射API 來操作這些資訊
獲得Class對象的方法
u如果一個類的執行個體已經得到,你可以使用
【Class c = 對象名.getClass(); 】
例: TextField t = new TextField();
Class c = t.getClass();
Class s = c.getSuperclass();
u如果你在編譯期知道類的名字,你可以使用如下的方法
Class c = java.awt.Button.class;
或者
Class c = Integer.TYPE;
u如果類名在編譯期不知道, 但是在運行期可以獲得, 你可以使用下面的方法
Class c = Class.forName(strg);
這樣獲得Class類對象的方法,其實是利用反射API把指定字串的類載入到記憶體中,所以也叫類載入器載入方法。這樣的話,它會把該類的靜態方法和靜態屬性,以及靜態代碼全部載入到記憶體中。但這時候,對象還沒有產生。所以為什麼靜態方法不能訪問非靜態屬性和方法。因為靜態方法和屬性產生的時機在非靜態屬性和方法之前。
程式碼範例:
package com; public class MyTest { public static void main(String[] args) { TestOne one=null; try{ Class cla=Class.forName("com.TestOne");//進行com.TestOne類載入,返回一個Class對象 System.out.println("********"); one=(TestOne)cla.newInstance();//產生這個Class類對象的一個執行個體,調用該類無參的構造方法,作用等同於new TestOne() }catch(Exception e){ e.printStackTrace(); } TestOne two=new TestOne(); System.out.println(one.getClass() == two.getClass());//比較兩個TestOne對象的Class對象是否是同一個對象,在這裡結果是true。說明如果兩個對象的類型相同,那麼它們會有相同的Class對象 }} class TestOne{ static{ System.out.println("靜態代碼塊運行"); } TestOne(){ System.out.println("構造方法"); }}
以上代碼過行的結果是:
靜態代碼塊運行
***********
構造方法
構造方法
程式碼分析:
在進行Class.forName("com.TestOne")的時候,實際上是對com.TestOne進行類載入,這時候,會把靜態屬性、方法以及靜態代碼塊都載入到記憶體中。所以這時候會列印出"靜態代碼塊運行"。但這時候,對象卻還沒有產生。所以"構造方法"這幾個字不會列印。當執行cla.newInstance()的時候,就是利用反射機制將Class對象產生一個該類的一個執行個體。這時候對象就產生了。所以列印"構造方法"。當執行到TestOne
two=new TestOne()語句時,又產生了一個對象。但這時候類已經載入完畢,靜態東西已經載入到記憶體中,而靜態代碼塊只執行一次,所以不用再去載入類,所以只會列印"構造方法",而"靜態代碼塊運行"不會列印。
反射機制不但可以例出該類對象所擁有的方法和屬性,還可以獲得該類的構造方法及通過構造方法獲得執行個體。也可以動態調用這個執行個體的成員方法。
程式碼範例:
package reflect; import java.lang.reflect.Constructor; /** * * 本類測試反射獲得類的構造器對象, * 並通過類構造器對象產生該類的執行個體 * */public class ConstructorTest { public static void main(String[] args) { try { //獲得指定字串類對象 Class cla=Class.forName("reflect.Tests"); //設定Class對象數組,用於指定構造方法類型 Class[] cl=new Class[]{int.class,int.class}; //獲得Constructor構造器對象。並指定構造方法類型 Constructor con=cla.getConstructor(cl); //給傳入參數賦初值 Object[] x={new Integer(33),new Integer(67)}; //得到執行個體 Object obj=con.newInstance(x); } catch (Exception e) { e.printStackTrace(); } } } class Tests{ public Tests(int x,int y){ System.out.println(x+" "+y); }}
啟動並執行結果是” 33 67”。說明我們已經產生了Tests這個類的一個對象。