1. 程式人生 > >Java中的反射機制(附例項程式)

Java中的反射機制(附例項程式)

一、什麼是反射: 
      反射的概念是由Smith在1982年首次提出的,主要是指程式可以訪問、檢測和修改它本身狀態或行為的一種能力。這一概念的提出很快引發了電腦科學領域關於應用反射性的研究。它首先被程式語言的設計領域所採用,並在Lisp和麵向物件方面取得了成績。其中LEAD/LEAD++ 、OpenC++ 、MetaXa和OpenJava等就是基於反射機制的語言。最近,反射機制也被應用到了視窗系統、作業系統和檔案系統中。 

      反射本身並不是一個新概念,儘管電腦科學賦予了反射概念新的含義。在電腦科學領域,反射是指一類應用,它們能夠自描述和自控制。也就是說,這類應用通過採用某種機制來實現對自己行為的描述(self-representation)和監測(examination),並能根據自身行為的狀態和結果,調整或修改應用所描述行為的狀態和相關的語義。 


二、什麼是Java中的類反射: 

       Reflection 是 Java 程式開發語言的特徵之一,它允許執行中的 Java 程式對自身進行檢查,或者說“自審”,並能直接操作程式的內部屬性和方法。Java 的這一能力在實際應用中用得不是很多,但是在其它的程式設計語言中根本就不存在這一特性。例如,Pascal、C 或者 C++ 中就沒有辦法在程式中獲得函式定義相關的資訊。 
Reflection 是 Java 被視為動態(或準動態)語言的關鍵,允許程式於執行期 Reflection APIs 取得任何已知名稱之 class 的內部資訊,包括 package、type parameters、superclass、implemented interfaces、inner classes, outer class, fields、constructors、methods、modifiers,並可於執行期生成instances、變更 fields 內容或喚起 methods。 


三、Java類反射中所必須的類: 
      Java的類反射所需要的類並不多,它們分別是:Field、Constructor、Method、Class、Object,下面我將對這些類做一個簡單的說明。 
Field類:提供有關類或介面的屬性的資訊,以及對它的動態訪問許可權。反射的欄位可能是一個類(靜態)屬性或例項屬性,簡單的理解可以把它看成一個封裝反射類的屬性的類。 
Constructor類:提供關於類的單個構造方法的資訊以及對它的訪問許可權。這個類和Field類不同,Field類封裝了反射類的屬性,而Constructor類則封裝了反射類的構造方法。 
Method類:提供關於類或介面上單獨某個方法的資訊。所反映的方法可能是類方法或例項方法(包括抽象方法)。 這個類不難理解,它是用來封裝反射類方法的一個類。 

Class類:類的例項表示正在執行的 Java 應用程式中的類和介面。列舉是一種類,註釋是一種介面。每個陣列屬於被對映為 Class 物件的一個類,所有具有相同元素型別和維數的陣列都共享該 Class 物件。 
Object類:每個類都使用 Object 作為超類。所有物件(包括陣列)都實現這個類的方法。 

四、Java的反射類能做什麼: 
       看完上面的這麼多我想你已經不耐煩了,你以為我在浪費你的時間,那麼好吧!下面我們就用一些簡單的小例子來說明它。 
       首先我們來看一下通過Java的反射機制我們能得到些什麼。 
       首先我們來寫一個類: 
  1. import java.awt.event.ActionListener;  
  2. import java.awt.event.ActionEvent;  
  3. class A extends Object implements ActionListener{  
  4. private int a = 3;  
  5. public Integer b = new Integer(4);  
  6. public A(){}  
  7. public A(int id,String name){}  
  8. public int abc(int id,String name){return 0;}  
  9. public void actionPerformed(ActionEvent e){}  
  10. }  

你可能被我這個類弄糊塗了,你看不出我要做什麼,那就不要看這個類了,這個類是用來測試的,你知道知道它繼承了Object類,有一個介面是ActionListener,兩個屬性int和Integer,兩個構造方法和兩個方法,這就足夠了。 
下面我們把A這個類作為一個反射類,來過去A類中的一些資訊,首先我們先來過去一下反射類中的屬性和屬性值。 
  1. import java.lang.reflect.*;  
  2. class B{  
  3. public static void main(String args[]){  
  4. A r = new A();  
  5. Class temp = r.getClass();  
  6. try{  
  7.          System.out.println("反射類中所有公有的屬性");  
  8.                          Field[] fb  =temp.getFields();  
  9. for(int j=0;j<fb.length;j++){  
  10. Class cl = fb[j].getType();  
  11. System.out.println("fb:"+cl);  
  12. }  
  13.                         System.out.println("反射類中所有的屬性");  
  14. Field[] fa = temp.getDeclaredFields();  
  15.                         for(int j=0;j<fa.length;j++){  
  16. Class cl = fa[j].getType();  
  17. System.out.println("fa:"+cl);  
  18. }  
  19.                            System.out.println("反射類中私有屬性的值");  
  20. Field f = temp.getDeclaredField("a");  
  21. f.setAccessible(true);  
  22. Integer i = (Integer)f.get(r);  
  23. System.out.println(i);  
  24. }catch(Exception e){  
  25. e.printStackTrace();  
  26. }  
  27. }  
  28. }  

這裡用到了兩個方法,getFields()、getDeclaredFields(),它們分別是用來獲取反射類中所有公有屬性和反射類中所有的屬性的方法。另外還有getField(String)和getDeclaredField(String)方法都是用來過去反射類中指定的屬性的方法,要注意的是getField方法只能取到反射類中公有的屬性,而getDeclaredField方法都能取到。 
這裡還用到了Field 類的setAccessible方法,它是用來設定是否有許可權訪問反射類中的私有屬性的,只有設定為true時才可以訪問,預設為false。另外 Field類還有set(Object AttributeName,Object value)方法,可以改變指定屬性的值。 

下面我們來看一下如何獲取反射類中的構造方法 
  1. import java.lang.reflect.*;  
  2. public class SampleConstructor {  
  3. public static void main(String[] args) {  
  4. A r = new A();  
  5. printConstructors(r);  
  6. }  
  7. public static void printConstructors(A r) {  
  8. Class c = r.getClass();  
  9. //獲取指定類的類名  
  10. String className = c.getName();  
  11. try {  
  12. //獲取指定類的構造方法  
  13. Constructor[] theConstructors = c.getConstructors();  
  14. for(int i=0; i<theConstructors.length; i++) {  
  15. //獲取指定構造方法的引數的集合  
  16. Class[] parameterTypes = theConstructors[i].getParameterTypes();  
  17. System.out.print(className + "(");  
  18. for(int j=0; j<parameterTypes.length; j++)  
  19. System.out.print(parameterTypes[j].getName() + " ");  
  20. System.out.println(")");  
  21. }  
  22. }catch(Exception e) {  
  23. e.printStackTrace();  
  24. }  
  25. }  
  26. }  


這個例子很簡單,只是用getConstructors()方法獲取了反射類的構造方法的集合,並用Constructor類的getParameterTypes()獲取該構造方法的引數。 

下面我們再來獲取一下反射類的父類(超類)和介面 
  1. import java.io.*;  
  2. import java.lang.reflect.*;  
  3. public class SampleInterface {  
  4. public static void main(String[] args) throws Exception {  
  5. A raf = new A();  
  6. printInterfaceNames(raf);  
  7. }  
  8. public static void printInterfaceNames(Object o) {  
  9. Class c = o.getClass();  
  10. //獲取反射類的介面  
  11. Class[] theInterfaces = c.getInterfaces();  
  12. for(int i=0; i<theInterfaces.length; i++)  
  13. System.out.println(theInterfaces[i].getName());  
  14. //獲取反射類的父類(超類)  
  15. Class theSuperclass = c.getSuperclass();  
  16. System.out.println(theSuperclass.getName());  
  17. }  
  18. }  

這個例子也很簡單,只是用Class類的getInterfaces()方法獲取反射類的所有介面,由於介面可以有多個,所以它返回一個 Class陣列。用getSuperclass()方法來獲取反射類的父類(超類),由於一個類只能繼承自一個類,所以它返回一個Class物件。 

下面我們來獲取一下反射類的方法 
  1. import java.lang.reflect.*;  
  2. public class SampleMethod {  
  3. public static void main(String[] args) {  
  4. A p = new A();  
  5. printMethods(p);  
  6. }  
  7. public static void printMethods(Object o) {  
  8. Class c = o.getClass();  
  9. String className = c.getName();  
  10. Method[] m = c.getMethods();  
  11. for(int i=0; i<m.length; i++) {  
  12. //輸出方法的返回型別  
  13. System.out.print(m[i].getReturnType().getName());  
  14. //輸出方法名  
  15. System.out.print(" "+m[i].getName()+"(");  
  16. //獲取方法的引數  
  17. Class[] parameterTypes = m[i].getParameterTypes();  
  18. for(int j=0; j<parameterTypes.length; j++){  
  19. System.out.print(parameterTypes[j].getName());  
  20. if(parameterTypes.length>j+1){  
  21. System.out.print(",");  
  22. }  
  23. }  
  24. System.out.println(")");  
  25. }  
  26. }  
  27. }  

這個例子並不難,它只是獲得了反射類的所有方法,包括繼承自它父類的方法。然後獲取方法的返回型別、方法名和方法引數。 

接下來讓我們回過頭來想一想,我們獲取了反射類的屬性、構造方法、父類、介面和方法,可這些東西能幫我們做些什麼呢!! 
下面我寫一個比較完整的小例子,來說明Java的反射類能做些什麼吧!! 
  1. import java.lang.reflect.Constructor;  
  2. import java.lang.reflect.Method;  
  3. public class LoadMethod {  
  4. public Object Load(String cName,String MethodName,String[] type,String[] param){  
  5. Object retobj = null;  
  6. try {  
  7.         //載入指定的Java類  
  8.             Class cls = Class.forName(cName);  
  9.             //獲取指定物件的例項  
  10.             Constructor ct = cls.getConstructor(null);  
  11.             Object obj = ct.newInstance(null);  
  12.             //構建方法引數的資料型別  
  13.             Class partypes[] = this.getMethodClass(type);  
  14.             //在指定類中獲取指定的方法  
  15.             Method meth = cls.getMethod(MethodName, partypes);  
  16.             //構建方法的引數值  
  17.             Object arglist[] = this.getMethodObject(type,param);  
  18.             //呼叫指定的方法並獲取返回值為Object型別  
  19.             retobj= meth.invoke(obj, arglist);  
  20.         }  
  21.         catch (Throwable e) {  
  22.             System.err.println(e);  
  23.         }  
  24. return retobj;  
  25. }  
  26. //獲取引數型別Class[]的方法  
  27. public Class[] getMethodClass(String[] type){  
  28. Class[] cs = new Class[type.length];  
  29. for (int i = 0; i < cs.length; i++) {  
  30. if(!type[i].trim().equals("")||type[i]!=null){  
  31. if(type[i].equals("int")||type[i].equals("Integer")){  
  32. cs[i]=Integer.TYPE;  
  33. }else if(type[i].equals("float")||type[i].equals("Float")){  
  34. cs[i]=Float.TYPE;  
  35. }else if(type[i].equals("double")||type[i].equals("Double")){  
  36. cs[i]=Double.TYPE;  
  37. }else if(type[i].equals("boolean")||type[i].equals("Boolean")){  
  38. cs[i]=Boolean.TYPE;  
  39. }else{  
  40. cs[i]=String.class;  
  41. }  
  42. }  
  43. }  
  44. return cs;  
  45. }  
  46. //獲取引數Object[]的方法  
  47. public Object[] getMethodObject(String[] type,String[] param){  
  48. Object[] obj = new Object[param.length];  
  49. for (int i = 0; i < obj.length; i++) {  
  50. if(!param[i].trim().equals("")||param[i]!=null){  
  51. if(type[i].equals("int")||type[i].equals("Integer")){  
  52. obj[i]= new Integer(param[i]);  
  53. }else if(type[i].equals("float")||type[i].equals("Float")){  
  54. obj[i]= new Float(param[i]);  
  55. }else if(type[i].equals("double")||type[i].equals("Double")){  
  56. obj[i]= new Double(param[i]);  
  57. }else if(type[i].equals("boolean")||type[i].equals("Boolean")){  
  58. obj[i]=new Boolean(param[i]);  
  59. }else{  
  60. obj[i] = param[i];  
  61. }  
  62. }  
  63. }  
  64. return obj;  
  65. }  
  66. }  

這是我在工作中寫的一個實現Java在執行時載入指定的類,並呼叫指定方法的一個小例子。這裡沒有main方法,你可以自己寫一個。 
Load方法接收的五個引數分別是,Java的類名,方法名,引數的型別和引數的值。 

結束語: 
Java語言反射提供一種動態連結程式元件的多功能方法。它允許程式建立和控制任何類的物件,無需提前硬編碼目標類。這些特性使得反射特別適用於建立以非常普通的方式與物件協作的庫。Java reflection 非常有用,它使類和資料結構能按名稱動態檢索相關資訊,並允許在執行著的程式中操作這些資訊。Java 的這一特性非常強大,並且是其它一些常用語言,如 C、C++、Fortran 或者 Pascal 等都不具備的。 

但反射有兩個缺點。第一個是效能問題。用於欄位和方法接入時反射要遠慢於直接程式碼。效能問題的程度取決於程式中是如何使用反射的。如果它作為程式執行中相對很少涉及的部分,緩慢的效能將不會是一個問題。即使測試中最壞情況下的計時圖顯示的反射操作只耗用幾微秒。僅反射在效能關鍵的應用的核心邏輯中使用時效能問題才變得至關重要。