熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> Java核心技術 >> 正文

Java動態程序設計——反射介紹

2013-11-23 19:01:23  來源: Java核心技術 

  Java動態程序設計反射介紹
  
  使用運行的類的信息使你的程序設計更加靈活
  
  反射授予了你的代碼訪問裝載進JVM內的Java類的內部信息的權限並且允許你編寫在程序執行期間與所選擇的類的一同工作的代碼而不是在源代碼中這種機制使得反射成為創建靈活的應用程序的強大工具但是要小心的是如果使用不恰當反射會帶來很大的副作用在這篇文章中軟件咨詢顧問Dennis Sosnoski 介紹了反射的使用同時還介紹了一些使用反射所要付出的代價在這裡你可以找到Java反射API是如何在運行時讓你鉤入對象的
  
  在第一部分我向你介紹了Java程序設計的類以及類的裝載那篇文章中描述了很多出現在Java二進制類格式中的信息現在我來介紹在運行時使用反射API訪問和使用這些信息的基礎為了使那些已經了解反射基礎的開發人員對這些事情感興趣我還會介紹一些反射與直接訪問的在性能方面的比較
  
  使用反射與和metadata(描述其它數據的數據)一些工作的Java程序設計是不同的通過Java語言反射來訪問的元數據的特殊類型是在JVM內部的類和對象的描述反射使你可以在運行時訪問各種類信息它甚至可以你讓在運行時讀寫屬性字段調用所選擇的類的方法
  
  反射是一個強大的工具它讓你建立靈活能夠在運行時組裝的代碼而不需要連接組件間的源代碼反射的一些特征也帶來一些問題在這章中我將會探究在應用程序中不打算使用反射的原因以為什麼使用它的原因在你了解到這些利弊之後你就會在好處大於缺點的時候做出決定
  
  初識class
  
  使用反射的起點總時一個javalangClass類的實例如果你與一個預先確定的類一同工作Java語言為直接獲得Class類的實例提供了一個簡單的快捷方式例如
  
  Class clas = MyClassclass;
  
  當你使用這項技術的時候所有與裝載類有關的工作都發生在幕後如果你需要在運行時從外部的資源中讀取類名使用上面這種方法是不會達到目的的相反你需要使用類裝載器來查找類的信息方法如下所示
  
  // name is the class name to load
  
  Class clas = null;
  
  try {
  
  clas = ClassforName(name);
  
  } catch (ClassNotFoundException ex) {
  
  // handle exception case
  
  }
  
  // use the loaded class
  
  如果類已經裝載你將會找到當前在在的類的信息如果類還沒有被裝載那麼類裝載器將會裝載它並且返回最近創建的類的實例
  
  關於類的反射
  
  Class對象給予你了所有的用於反射訪問類的元數據的基本鉤子這些元數據包括有關類的自身信息例如象類的包和子類還有這個類所實現的接口還包括這個類所定義的構造器屬性字段以及方法的詳細信息後面的這些項是我們在程序設計過種經常使用的因此在這一節的後面我會給出一些用這些信息來工作的例子
  
  對於類的構造中的每一種類型(構造器屬性字段方法)javalangClass提供了四種獨立的反射調用以不的方式來訪問類的信息下面列出了這四種調用的標准形式它是一組用於查找構造器的調用
  
  Constructor getConstructor(Class[] params)  使用指定的參數類型來獲得公共的構造器
  
  Constructor[] getConstructors()  獲得這個類的所有構造器
  
  Constructor getDeclaredConstructor(Class[] params) 使用指定的參數類型來獲得構造器(忽略訪問的級別)
  
  Constructor[] getDeclaredConstructors() 獲得這個類的所有的構造器(忽略訪問的級別)
  
  上述的每一種方法都返回一或多個javalangreflectConstructor的實例Constructor類定義了一個需要一個對象數據做為唯一參數的newInstance方法然後返回一個最近創建的原始類的實例對象數組是在構造器調用時所使用的參數值例如假設你有一個帶有一對String 類型做為參數的構造器的TwoString類代碼如下所示
  
  public class TwoString {
  
  private String m_s m_s;
  
  public TwoString(String s String s) {
  
  m_s = s;
  
  m_s = s;
  
  }
  
  }
  
  下面的代碼顯示如何獲得TwoString類的構造器並使用字符串ab來創建一個實例
  
  Class[] types = new Class[] { Stringclass Stringclass };
  
  Constructor cons = TwoStringclassgetConstructor(types);
  
  Object[] args = new Object[] { a b };
  
  TwoString ts = consnewInstance(args);
  
  上面的代碼忽略了幾種可能的被不同的反射方法拋出的異常檢查的類型這些異常在Javadoc API中有詳細的描述因此為簡便起見我會在所有的代碼中忽略它們
  
  在我涉及到構造器這個主題時Java語言也定義了一個特殊的沒有參數的(或默認)構造器快捷方法你能使用它來創建一個類的實例這個快捷方法象下面的代碼這樣被嵌入到類的自定義中
  
  Object newInstance() ?使用默認的構造器創建新的實例
  
  盡管這種方法只讓你使用一個特殊的構造器但是如果你需要的話它是非常便利的快捷方式這項技術在使用JavaBeans工作的時候尤其有用因為JavaBeans需要定義一個公共的沒有參數的構造器
  
  通過反射來查找屬性字段
  
  Class類反射調用訪問屬性字段信息與那些用於訪問構造器的方法類似在有數組類型的參數的使用屬性字段名來替代使用方法如下所示
  
  Field getField(String name) 獲得由name指定的具有public級別的屬性字段
  
  Field getFields() ?獲得一個類的所有具有public級別的屬性字段
  
  Field getDeclaredField(String name) ?獲得由name指定的被類聲明的屬性字段
  
  Field getDeclaredFields() ?獲得由類定義的所有的屬性字段
  
  盡管與構造器的調用很相似但是在提到屬性字段的時候有一個重要的差別前兩個方法返回能過類來訪問的公共(public)屬性字段的信息(包括那些來自於超類的屬性字段)後兩個方法返回由類直接聲明的所有的屬性字段(忽略了屬性字段的訪問類型)
  
  JavalangreflectField的實例通過調用定義好的getXXX和setXXX方法來返回所有的原始的數據類型就像普通的與對象引用一起工作的get和set方法一樣盡管getXXX方法會自動地處理數據類型轉換(例如使用getInt方法來獲取一個byte類型的值)但使用一個適當基於實際的屬性字段類型的方法是應該優先考慮的
  
  下面的代碼顯示了如何使用屬性字段的反射方法通過指定屬性字段名找到一個對象的int類型的屬性字段並給這個屬性字段值加
  
  public int incrementField(String name Object obj) throws {
  
  Field field = objgetClass()getDeclaredField(name);
  
  int value = fieldgetInt(obj) + ;
  
  fieldsetInt(obj value);
  
  return value;
  
  }
  
  這個方法開始展現一些使用反射所可能帶來的靈活性它優於與一個特定的類一同工作incrementField方法把要查找的類信息的對象傳遞給getClass方法然後直接在那個類中查找命名的屬性字段
  
  通過反射來查找方法
  
  Class反射調用訪問方法的信息與訪問構造器和字段屬性的方法非常相似
  
  Method getMethod(String nameClass[] params) 使用指定的參數類型獲得由name參數指定的public類型的方法
  
  Mehtod[] getMethods()?獲得一個類的所有的public類型的方法
  
  Mehtod getDeclaredMethod(String name Class[] params)?使用指定的參數類型獲得由name參數所指定的由這個類聲明的方法
  
  Method[] getDeclaredMethods() ?獲得這個類所聲明的所有的方法
  
  與屬性字段的調用一樣前兩個方法返回通過這個類的實例可以訪問的public類型的方法?包括那些繼承於超類的方法後兩個方法返回由這個類直接聲明的方法的信息而不管方法的訪問類型
  
  通過調用返回的JavalangreflectMehtod實例定義了一個invoke方法你可以使用它來調用定義類的有關實例這個invoke方法需要兩個參數一個是提供這個方法的類的實例一個是調用這個方法所需要的參數值的數組
  
  下面給出了比屬性字段的例子更加深入的例子它顯示了一個的方法反射的例子這個方法使用get和set方法來給JavaBean定義的int類型的屬性做增量操作例如如果對象為一個整數類型count屬性定義了getCount和setCount方法那麼為了給這個屬性做增量運算你就可以把count做為參數名傳遞給調用的這個方法中示例代碼如下
  
  public int incrementProperty(String name Object obj) {
  
  String prop = CharactertoUpperCase(namecharAt()) +
  
  namesubstring();
  
  String mname = get + prop;
  
  Class[] types = new Class[] {};
  
  Method method = objgetClass()getMethod(mname types);
  
  Object result = methodinvoke(obj new Object[]);
  
  int value = ((Integer)result)intValue() + ;
  
  mname =
From:http://tw.wingwit.com/Article/program/Java/hx/201311/26154.html
    推薦文章
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.