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

Java Reflection (JAVA反射)詳解

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

  Reflection是Java 程序開發語言的特征之一它允許運行中的 Java 程序對自身進行檢查或者說自審並能直接操作程序的內部屬性例如使用它能獲得 Java 類中各成員的名稱並顯示出來

  Java 的這一能力在實際應用中也許用得不是很多但是在其它的程序設計語言中根本就不存在這一特性例如PascalC 或者 C++  中就沒有辦法在程序中獲得函數定義相關的信息

  JavaBean 是 reflection 的實際應用之一它能讓一些工具可視化的操作軟件組件這些工具通過 reflection 動態的載入並取得 Java 組件(類) 的屬性

   一個簡單的例子

  考慮下面這個簡單的例子讓我們看看 reflection 是如何工作的

  

  

  import javalangreflect*;
public class DumpMethods {
public static void main(String args[]) {
try {
Class c = ClassforName(args[]);
Method m[] = cgetDeclaredMethods();
for (int i = ; i < mlength; i++)
Systemoutprintln(m[i]toString());
} catch (Throwable e) {
Systemerrprintln(e);
}
}
}

  按如下語句執行

  java DumpMethods javautilStack

  它的結果輸出為

  

  public javalangObject javautilStackpush(javalangObject)

public synchronized javalangObject javautilStackpop()

public synchronized javalangObject javautilStackpeek()

public boolean javautilStackempty()

public synchronized int javautilStacksearch(javalangObject)

  這樣就列出了javautilStack 類的各方法名以及它們的限制符和返回類型

  這個程序使用 ClassforName 載入指定的類然後調用 getDeclaredMethods 來獲取這個類中定義了的方法列表javalangreflectMethods 是用來描述某個類中單個方法的一個類

  開始使用 Reflection

  用於 reflection 的類如 Method可以在 javalangrelfect 包中找到使用這些類的時候必須要遵循三個步驟第一步是獲得你想操作的類的 javalangClass 對象在運行中的 Java 程序中用 javalangClass 類來描述類和接口等

  下面就是獲得一個 Class 對象的方法之一

  Class c = ClassforName(javalangString

  這條語句得到一個 String 類的類對象還有另一種方法如下面的語句

  Class c = intclass

  或者

  Class c = IntegerTYPE

  它們可獲得基本類型的類信息其中後一種方法中訪問的是基本類型的封裝類 (如 Integer) 中預先定義好的 TYPE 字段

  第二步是調用諸如 getDeclaredMethods 的方法以取得該類中定義的所有方法的列表

  一旦取得這個信息就可以進行第三步了——使用 reflection API 來操作這些信息如下面這段代碼

  Class c = ClassforName(javalangString

  Method m[] = cgetDeclaredMethods()

  Systemoutprintln(m[]toString())

  它將以文本方式打印出 String 中定義的第一個方法的原型

  在下面的例子中這三個步驟將為使用 reflection 處理特殊應用程序提供例證

  模擬 instanceof 操作符

  得到類信息之後通常下一個步驟就是解決關於 Class 對象的一些基本的問題例如ClassisInstance 方法可以用於模擬 instanceof 操作符

  

  class A {
}

public class instance {
public static void main(String args[]) {
try {
Class cls = ClassforName(A);
boolean b = clsisInstance(new Integer());
Systemoutprintln(b);
boolean b = clsisInstance(new A());
Systemoutprintln(b);
} catch (Throwable e) {
Systemerrprintln(e);
}
}
}

  在這個例子中創建了一個 A 類的 Class 對象然後檢查一些對象是否是 A 的實例Integer() 不是但 new A()是

  找出類的方法

  找出一個類中定義了些什麼方法這是一個非常有價值也非常基礎的 reflection 用法下面的代碼就實現了這一用法

  

  

  import javalangreflect*;

public class method {
private int f(Object p int x) throws NullPointerException {
if (p == null)
throw new NullPointerException();
return x;
}

public static void main(String args[]) {
try {
Class cls = ClassforName(method);
Method methlist[] = clsgetDeclaredMethods();
for (int i = ; i < methlistlength; i++) {
Method m = methlist[i];
Systemoutprintln(name = + mgetName());
Systemoutprintln(decl class = + mgetDeclaringClass());
Class pvec[] = mgetParameterTypes();
for (int j = ; j < pveclength; j++)
Systemoutprintln(param # + j + + pvec[j]);
Class evec[] = mgetExceptionTypes();
for (int j = ; j < eveclength; j++)
Systemoutprintln(exc # + j + + evec[j]);
Systemoutprintln(return type = + mgetReturnType());
Systemoutprintln();
}
} catch (Throwable e) {
Systemerrprintln(e);
}
}
}

  這個程序首先取得 method 類的描述然後調用 getDeclaredMethods 來獲取一系列的 Method 對象它們分別描述了定義在類中的每一個方法包括 public 方法protected 方法package 方法和 private 方法等如果你在程序中使用 getMethods 來代替 getDeclaredMethods你還能獲得繼承來的各個方法的信息

  取得了 Method 對象列表之後要顯示這些方法的參數類型異常類型和返回值類型等就不難了這些類型是基本類型還是類類型都可以由描述類的對象按順序給出

  輸出的結果如下

  

  

  name = f

decl class = class method


 param # class javalangObject

param # int

exc # class javalangNullPointerException

return type = int


name = main

decl class = class method

param # class [LjavalangString;

return type = void

  獲取構造器信息

  獲取類構造器的用法與上述獲取方法的用法類似

  

  import javalangreflect*;

public class constructor {
public constructor() {
}

protected constructor(int i double d) {
}

public static void main(String args[]) {
try {
Class cls = ClassforName(constructor);
Constructor ctorlist[] = clsgetDeclaredConstructors();
for (int i = ; i < ctorlistlength; i++) {
Constructor ct = ctorlist[i];
Systemoutprintln(name = + ctgetName());
Systemoutprintln(decl class = + ctgetDeclaringClass());
Class pvec[] = ctgetParameterTypes();
for (int j = ; j < pveclength; j++)
Systemoutprintln(param # + j + + pvec[j]);
Class evec[] = ctgetExceptionTypes();
for (int j = ; j < eveclength; j++)
Systemoutprintln(exc # + j + + evec[j]);
Systemoutprintln();
}
} catch (Throwable e) {
Systemerrprintln(e);
}
}
}

  這個例子中沒能獲得返回類型的相關信息那是因為構造器沒有返回類型

  這個程序運行的結果是

  

  

  name = constructor

decl class = class constructor


name = constructor

decl class = class constructor

param # int

param # double

  獲取類的字段(域)

  找出一個類中定義了哪些數據字段也是可能的下面的代碼就在干這個事情

  

  

  import javalangreflect*;

public class field {
private double d;
public static final int i = ;
String s = testing;


 public static void main(String args[]) {
try {
Class cls = ClassforName(field);
Field fieldlist[] = clsgetDeclaredFields();
for (int i = ; i < fieldlistlength; i++) {
Field fld = fieldlist[i];
Systemoutprintln(name = + fldgetName());
Systemoutprintln(decl class = + fldgetDeclaringClass());
Systemoutprintln(type = + fldgetType());
int mod = fldgetModifiers();
Systemoutprintln(modifiers = + ModifiertoString(mod));
Systemoutprintln();
}
} catch (Throwable e) {
Systemerrprintln(e);
}
}
}

  這個例子和前面那個例子非常相似例中使用了一個新東西 Modifier它也是一個 reflection 類用來描述字段成員的修飾語private int這些修飾語自身由整數描述而且使用 ModifiertoString 來返回以官方順序排列的字符串描述 (如staticfinal之前)這個程序的輸出是

  

  

  name = d

decl class = class field

type = double

modifiers = private


name = i

decl class = class field

type = int

modifiers = public static final


name = s

decl class = class field

type = class javalangString

modifiers =

  和獲取方法的情況一下獲取字段的時候也可以只取得在當前類中申明了的字段信息 (getDeclaredFields)或者也可以取得父類中定義的字段 (getFields)

  根據方法的名稱來執行方法

  文本到這裡所舉的例子無一例外都與如何獲取類的信息有關我們也可以用 reflection 來做一些其它的事情比如執行一個指定了名稱的方法下面的示例演示了這一操作

  

  

  import javalangreflect*;
public class method {
public int add(int a int b) {
return a + b;
}
public static void main(String args[]) {
try {
Class cls = ClassforName(method);
Class partypes[] = new Class[];
partypes[] = IntegerTYPE;
partypes[] = IntegerTYPE;
Method meth = clsgetMethod(add partypes);
method methobj = new method();
Object arglist[] = new Object[];
arglist[] = new Integer();
arglist[] = new Integer();
Object retobj = methinvoke(methobj arglist);
Integer retval = (Integer) retobj;
Systemoutprintln(retvalintValue());
} catch (Throwable e) {
Systemerrprintln(e);
}
}
}

  假如一個程序在執行的某處的時候才知道需要執行某個方法這個方法的名稱是在程序的運行過程中指定的 (例如JavaBean 開發環境中就會做這樣的事)那麼上面的程序演示了如何做到

  上例中getMethod用於查找一個具有兩個整型參數且名為 add 的方法找到該方法並創建了相應的Method 對象之後在正確的對象實例中執行它執行該方法的時候需要提供一個參數列表這在上例中是分別包裝了整數 的兩個 Integer 對象執行方法的返回的同樣是一個 Integer 對象它封裝了返回值

  創建新的對象

  對於構造器則不能像執行方法那樣進行因為執行一個構造器就意味著創建了一個新的對象 (准確的說創建一個對象的過程包括分配內存和構造對象)所以與上例最相似的例子如下

  

  import javalangreflect*;

public class constructor {
public constructor() {
}

public constructor(int a int b) {
Systemoutprintln(a = + a + b = + b);
}

public static void main(String args[]) {
try {
Class cls = ClassforName(constructor);
Class partypes[] = new Class[];
partypes[] = IntegerTYPE;
partypes[] = IntegerTYPE;
Constructor ct = clsgetConstructor(partypes);
Object arglist[] = new Object[];
arglist[] = new Integer();
arglist[] = new Integer();
Object retobj = ctnewInstance(arglist);
} catch (Throwable e) {
Systemerrprintln(e);
}
}
}

  根據指定的參數類型找到相應的構造函數並執行它以創建一個新的對象實例使用這種方法可以在程序運行時動態地創建對象而不是在編譯的時候創建對象這一點非常有價值

  改變字段(域)的值

  reflection 的還有一個用處就是改變對象數據字段的值reflection 可以從正在運行的程序中根據名稱找到對象的字段並改變它下面的例子可以說明這一點

  

  import javalangreflect*;

public class field {
public double d;

public static void main(String args[]) {
try {
Class cls = ClassforName(field);
Field fld = clsgetField(d);
field fobj = new field();
Systemoutprintln(d = + fobjd);
fldsetDouble(fobj );
Systemoutprintln(d = + fobjd);
} catch (Throwable e) {
Systemerrprintln(e);
}
}
}

  這個例子中字段 d 的值被變為了

  使用數組

  本文介紹的 reflection 的最後一種用法是創建的操作數組數組在 Java 語言中是一種特殊的類類型一個數組的引用可以賦給 Object 引用觀察下面的例子看看數組是怎麼工作的

  

  import javalangreflect*;

public class array {
public static void main(String args[]) {
try {
Class cls = ClassforName(javalangString);
Object arr = ArraynewInstance(cls );
Arrayset(arr this is a test);
String s = (String) Arrayget(arr );
Systemoutprintln(s);
} catch (Throwable e) {
Systemerrprintln(e);
}
}
}

  例中創建了 個單位長度的 String 數組為第 個位置的字符串賦了值最後將這個字符串從數組中取得並打印了出來

  下面這段代碼提供了一個更復雜的例子

  

  import javalangreflect*;

public class array {
public static void main(String args[]) {
int dims[] = new int[]{ };
Object arr = ArraynewInstance(IntegerTYPE dims);
Object arrobj = Arrayget(arr );
Class cls = arrobjgetClass()getComponentType();
Systemoutprintln(cls);
arrobj = Arrayget(arrobj );
ArraysetInt(arrobj );
int arrcast[][][] = (int[][][]) arr;
Systemoutprintln(arrcast[][][]);
}
}

  例中創建了一個 x x 的整型數組並為處於 [][][] 的元素賦了值為 注意多維數組實際上就是數組的數組例如第一個 Arrayget 之後arrobj 是一個 x 的數組進而取得其中的一個元素即長度為 的數組並使用 ArraysetInt 為它的第 個元素賦值

  注意創建數組時的類型是動態的在編譯時並不知道其類型


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