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

Java反射機制深入研究

2022-06-13   來源: Java核心技術 

  Java 反射是Java語言的一個很重要的特征它使得Java具體了動態性

  在Java運行時環境中對於任意一個類能否知道這個類有哪些屬性和方法?對於任意一個對象能否調用它的任意一個方法?答案是肯定的這種動態獲取類的信息以及動態調用對象的方法的功能來自於Java 語言的反射(Reflection)機制

  Java 反射機制主要提供了以下功能

  在運行時判斷任意一個對象所屬的類

  在運行時構造任意一個類的對象

  在運行時判斷任意一個類所具有的成員變量和方法

  在運行時調用任意一個對象的方法

  Reflection 是Java被視為動態(或准動態)語言的一個關鍵性質這個機制允許程序在運行時透過Reflection APIs取得任何一個已知名稱的class的內部信息包括其modifiers(諸如public static 等等)superclass(例如Object)實現之interfaces(例如Serializable)也包括fields和methods 的所有信息並可於運行時改變fields內容或調用methods

  一般而言開發者社群說到動態語言大致認同的一個定義是程序運行時允許改變程序結構或變量類型這種語言稱為動態語言從這個觀點看PerlPythonRuby是動態語言C++JavaC#不是動態語言

  盡管在這樣的定義與分類下Java不是動態語言它卻有著一個非常突出的動態相關機制Reflection這個字的意思是反射映象倒 影用在Java身上指的是我們可以於運行時加載探知使用編譯期間完全未知的classes換句話說Java程序可以加載一個運行時才得知名稱 的class獲悉其完整構造(但不包括methods定義)並生成其對象實體或對其fields設值或喚起其methods這種看透 class的能力(the ability of the program to examine itself)被稱為introspection(內省內觀反省)Reflection和introspection是常被並提的兩個術語

  在JDK中主要由以下類來實現Java反射機制這些類都位於javalangreflect包中

  Class類代表一個類

  Field 類代表類的成員變量(成員變量也稱為類的屬性)

  Method類代表類的方法

  Constructor 類代表類的構造方法

  Array類提供了動態創建數組以及訪問數組的元素的靜態方法

  下面給出幾個例子看看Reflection API的實際運用

  一通過Class類獲取成員變量成員方法接口超類構造方法等

  在javalangObject 類中定義了getClass()方法因此對於任意一個Java對象都可以通過此方法獲得對象的類型Class類是Reflection API 中的核心類它有以下方法

  getName()獲得類的完整名字

  getFields()獲得類的public類型的屬性

  getDeclaredFields()獲得類的所有屬性

  getMethods()獲得類的public類型的方法

  getDeclaredMethods()獲得類的所有方法

  getMethod(String name Class[] parameterTypes)獲得類的特定方法name參數指定方法的名字parameterTypes 參數指定方法的參數類型

  getConstructors()獲得類的public類型的構造方法

  getConstructor(Class[] parameterTypes)獲得類的特定構造方法parameterTypes 參數指定構造方法的參數類型

  newInstance()通過類的不帶參數的構造方法創建這個類的一個對象

  下面給出一個綜合運用的例子

  public class RefConstructor {

  public static void main(String args[]) throws Exception {

  RefConstructor ref = new RefConstructor();

  refgetConstructor();

  }

  public void getConstructor() throws Exception {

  Class c = null;

  c = ClassforName(javalangLong);

  Class cs[] = {javalangStringclass};

  Systemoutprintln(\n\n);

  Constructor cst = cgetConstructor(cs);

  Systemoutprintln(通過參數獲取指定Class對象的構造方法);

  Systemoutprintln(csttoString());

  Constructor cst = cgetDeclaredConstructor(cs);

  Systemoutprintln(通過參數獲取指定Class對象所表示的類或接口的構造方法);

  Systemoutprintln(csttoString());

  Constructor cst = cgetEnclosingConstructor();

  Systemoutprintln(獲取本地或匿名類Constructor 對象它表示基礎類的立即封閉構造方法);

  if (cst != null) Systemoutprintln(csttoString());

  else Systemoutprintln( 沒有獲取到任何構造方法!);

  Constructor[] csts = cgetConstructors();

  Systemoutprintln(獲取指定Class對象的所有構造方法);

  for (int i = ; i < cstslength; i++) {

  Systemoutprintln(csts[i]toString());

  }

  Systemoutprintln(\n\n);

  Type types[] = cgetGenericInterfaces();

  Systemoutprintln(返回直接實現的接口);

  for (int i = ; i < typeslength; i++) {

  Systemoutprintln(types[i]toString());

  }

  Type type = cgetGenericSuperclass();

  Systemoutprintln(返回直接超類);

  Systemoutprintln(typetoString());

  Class[] cis = cgetClasses();

  Systemoutprintln(返回超類和所有實現的接口);

  for (int i = ; i < cislength; i++) {

  Systemoutprintln(cis[i]toString());

  }

  Class cs[] = cgetInterfaces();

  Systemoutprintln(實現的接口);

  for (int i = ; i < cslength; i++) {

  Systemoutprintln(cs[i]toString());

  }

  Systemoutprintln(\n\n);

  Field fs[] = cgetFields();

  Systemoutprintln(類或接口的所有可訪問公共字段);

  for (int i = ; i < fslength; i++) {

  Systemoutprintln(fs[i]toString());

  }

  Field f = cgetField(MIN_VALUE);

  Systemoutprintln(類或接口的指定已聲明指定公共成員字段);

  Systemoutprintln(ftoString());

  Field fs[] = cgetDeclaredFields();

  Systemoutprintln(類或接口所聲明的所有字段);

  for (int i = ; i < fslength; i++) {

  Systemoutprintln(fs[i]toString());

  }

  Field f = cgetDeclaredField(serialVersionUID);

  Systemoutprintln(類或接口的指定已聲明指定字段);

  Systemoutprintln(ftoString());

  Systemoutprintln(\n\n);

  Method m[] = cgetMethods();

  Systemoutprintln(返回類所有的公共成員方法);

  for (int i = ; i < mlength; i++) {

  Systemoutprintln(m[i]toString());

  }

  Method m = cgetMethod(longValue new Class[]{});

  Systemoutprintln(返回指定公共成員方法);

  Systemoutprintln(mtoString());

  }

  }

  輸出結果輸出結果很長這裡不再給出

  二運行時復制對象

  例程ReflectTester 類進一步演示了Reflection API的基本使用方法ReflectTester類有一個copy(Object object)方法這個方法能夠創建一個和參數object 同樣類型的對象然後把object對象中的所有屬性拷貝到新建的對象中並將它返回

  這個例子只能復制簡單的JavaBean假定JavaBean 的每個屬性都有public 類型的getXXX()和setXXX()方法

  public class ReflectTester {

  public Object copy(Object object) throws Exception {

  // 獲得對象的類型

  Class<?> classType = objectgetClass();

  Systemoutprintln(Class: + classTypegetName());

  // 通過默認構造方法創建一個新的對象

  Object objectCopy = classTypegetConstructor(new Class[]{})newInstance(new Object[]{});

  // 獲得對象的所有屬性

  Field fields[] = classTypegetDeclaredFields();

  for (int i = ; i < fieldslength; i++) {

  Field field = fields[i];

  String fieldName = fieldgetName();

  String firstLetter = fieldNamesubstring( )toUpperCase();

  // 獲得和屬性對應的getXXX()方法的名字

  String getMethodName = get + firstLetter + fieldNamesubstring();

  // 獲得和屬性對應的setXXX()方法的名字

  String setMethodName = set + firstLetter + fieldNamesubstring();

  // 獲得和屬性對應的getXXX()方法

  Method getMethod = classTypegetMethod(getMethodName new Class[]{});

  // 獲得和屬性對應的setXXX()方法

  Method setMethod = classTypegetMethod(setMethodName new Class[]{fieldgetType()});

  // 調用原對象的getXXX()方法

  Object value = getMethodinvoke(object new Object[]{});

  Systemoutprintln(fieldName + : + value);

  // 調用拷貝對象的setXXX()方法

  setMethodinvoke(objectCopy new Object[]{value});

  }

  return objectCopy;

  }

  public static void main(String[] args) throws Exception {

  Customer customer = new Customer(Tom );

  customersetId(new Long());

  Customer customerCopy = (Customer) new ReflectTester(py(customer);

  Systemoutprintln(Copy information: + customerCopygetId() + + customerCopygetName() +

  + customerCopygetAge());

  }

  }

  class Customer {

  private Long id;

  private String name;

  private int age;

  public Customer() {

  }

  public Customer(String name int age) {

  thisname = name;

  thisage = age;

  }

  public Long getId() {

  return id;

  }

  public void setId(Long id) {

  thisid = id;

  }

  public String getName() {

  return name;

  }

  public void setName(String name) {

  thisname = name;

  }

  public int getAge() {

  return age;

  }

  public void setAge(int age) {

  thisage = age;

  }

  }

  輸出結果

  Class:comlangsinreflectionCustomer

  id:

  name:Tom

  age:

  Copy information: Tom

  Process finished with exit code

  解說

  ReflectTester 類的copy(Object object)方法依次執行以下步驟

  ()獲得對象的類型

  Class classType=objectgetClass();

  Systemoutprintln(Class:+classTypegetName());

  ()通過默認構造方法創建一個新對象

  Object objectCopy=classTypegetConstructor(new Class[]{})newInstance(new Object[]{});

  以上代碼先調用Class類的getConstructor()方法獲得一個Constructor 對象它代表默認的構造方法然後調用Constructor對象的newInstance()方法構造一個實例

  )獲得對象的所有屬性

  Field fields[]=classTypegetDeclaredFields();

  Class 類的getDeclaredFields()方法返回類的所有屬性包括publicprotected默認和private訪問級別的屬性

  ()獲得每個屬性相應的getXXX()和setXXX()方法然後執行這些方法把原來對象的屬性拷貝到新的對象中

  三用反射機制調用對象的方法

  public class InvokeTester {

  public int add(int param int param) {

  return param + param;

  }

  public String echo(String msg) {

  return echo: + msg;

  }

  public static void main(String[] args) throws Exception {

  Class<?> classType = InvokeTesterclass;

  Object invokeTester = classTypenewInstance();

  // Object invokeTester = classTypegetConstructor(new

  // Class[]{})newInstance(new Object[]{});

  //獲取InvokeTester類的add()方法

  Method addMethod = classTypegetMethod(add new Class[]{intclass intclass});

  //調用invokeTester對象上的add()方法

  Object result = addMethodinvoke(invokeTester new Object[]{new Integer() new Integer()});

  Systemoutprintln((Integer) result);

  //獲取InvokeTester類的echo()方法

  Method echoMethod = classTypegetMethod(echo new Class[]{Stringclass});

  //調用invokeTester對象的echo()方法

  result = echoMethodinvoke(invokeTester new Object[]{Hello});

  Systemoutprintln((String) result);

  }

  }

  在例程InvokeTester類的main()方法中運用反射機制調用一個InvokeTester對象的add()和echo()方法

  add()方法的兩個參數為int 類型獲得表示add()方法的Method對象的代碼如下

  Method addMethod=classTypegetMethod(addnew Class[]{intclassintclass});

  Method類的invoke(Object objObject args[])方法接收的參數必須為對象如果參數為基本類型數據必須轉換為相應的包裝類型的對象invoke()方法的返回值總是對象如果實際被 調用的方法的返回類型是基本類型數據那麼invoke()方法會把它轉換為相應的包裝類型的對象再將其返回

  在本例中盡管InvokeTester 類的add()方法的兩個參數以及返回值都是int類型調用add Method 對象的invoke()方法時只能傳遞Integer 類型的參數並且invoke()方法的返回類型也是Integer 類型Integer 類是int 基本類型的包裝類

  Object result=addMethodinvoke(invokeTester

  new Object[]{new Integer()new Integer()});

  Systemoutprintln((Integer)result); //result 為Integer類型

  四動態創建和訪問數組

  javalangArray 類提供了動態創建和訪問數組元素的各種靜態方法

  例程ArrayTester 類的main()方法創建了一個長度為 的字符串數組接著把索引位置為 的元素設為hello然後再讀取索引位置為 的元素的值

  public class ArrayTester {

  public static void main(String args[]) throws Exception {

  Class<?> classType = ClassforName(javalangString);

  // 創建一個長度為的字符串數組

  Object array = ArraynewInstance(classType );

  // 把索引位置為的元素設為hello

  Arrayset(array hello);

  // 獲得索引位置為的元素的值

  String s = (String) Arrayget(array );

  Systemoutprintln(s);

  }

  }

  例程ArrayTester 類的main()方法創建了一個 x x 的整型數組並把索引位置為[][][] 的元素的值為設

  public class ArrayTester {

  public static void main(String args[]) {

  int[] dims = new int[]{ };

  //創建一個具有指定的組件類型和維度的新數組

  Object array = ArraynewInstance(IntegerTYPE dims);

  Object arrayObj = Arrayget(array );

  Class<?> cls = arrayObjgetClass()getComponentType();

  Systemoutprintln(cls);

  arrayObj = Arrayget(arrayObj );

  ArraysetInt(arrayObj );

  int arrayCast[][][] = (int[][][]) array;

  Systemoutprintln(arrayCast[][][]);

  }

  }

  深入認識Class類

  眾所周知Java有個Object類是所有Java類的繼承根源其內聲明了數個應該在所有Java類中被改寫的方 法hashCode()equals()clone()toString()getClass()等其中getClass()返回一個 Class類的對象

  Class類十分特殊它和一般classes一樣繼承自Object其實體用以表達Java程序運行時的classes和interfaces也用來表達enumarrayprimitive Java types

  (boolean byte char short int long float double)以及關鍵詞void當一個class被加載或當加載器(class loader)的defineClass()被JVM調用JVM 便自動產生一個Class object如果您想借由修改Java標准庫源碼來觀察Class object的實際生成時機(例如在Class的constructor內添加一個println())不能夠!因為Class並沒有public constructor

  Class是Reflection起源針對任何您想探勘的class唯有先為它產生一個Class object接下來才能經由後者喚起為數十多個的Reflection APIs

  Java允許我們從多種途徑為一個class生成對應的Class對象參看本人的《 深入研究javalongClass類 <; 》一文

  欲生成對象實體在Reflection 動態機制中有兩種作法一個針對無自變量ctor一個針對帶參數ctor如果欲調用的是帶參數ctor就比較麻煩些不再調用Class 的newInstance()而是調用Constructor 的newInstance()首先准備一個Class[]做為ctor的參數類型(本例指定

  為一個double和一個int)然後以此為自變量調用getConstructor()獲得一個專屬ctor接下來再准備一個Object[] 做為ctor實參值(本例指定調用上述專屬ctor的newInstance()

  動態生成Class object 所對應之class的對象實體無自變量

  這個動作和上述調用帶參數之ctor相當類似首先准備一個Class[]做為參數類型(本例指定其中一個是String另一個是 Hashtable)然後以此為自變量調用getMethod()獲得特定的Method object接下來准備一個Object[]放置自變量然後調用上述所得之特定Method object的invoke()

  為什麼獲得Method object時不需指定回返類型?

  因為method overloading機制要求signature必須唯一而回返類型並非signature的一個成份換句話說只要指定了method名稱和參數列就一定指出了一個獨一無二的method

  四運行時變更field內容

  與先前兩個動作相比變更field內容輕松多了因為它不需要參數和自變量首先調用Class的getField()並指定field名稱獲得特定的Field object之後便可直接調用Field的get()和set()

  public class RefFiled {

  public double x;

  public Double y;

  public static void main(String args[]) throws NoSuchFieldException IllegalAccessException {

  Class c = RefFiledclass;

  Field xf = cgetField(x);

  Field yf = cgetField(y);

  RefFiled obj = new RefFiled();

  Systemoutprintln(變更前x= + xfget(obj));

  //變更成員x值

  xfset(obj );

  Systemoutprintln(變更後x= + xfget(obj));

  Systemoutprintln(變更前y= + yfget(obj));

  //變更成員y值

  yfset(obj );

  Systemoutprintln(變更後y= + yfget(obj));

  }

  }

  運行結果

  變更前x=

  變更後x=

  變更前y=null

  變更後y=

  Process finished with exit code


From:http://tw.wingwit.com/Article/program/Java/hx/201311/26838.html
    Copyright © 2005-2022 電腦知識網 Computer Knowledge   All rights reserved.