java語言

當前位置 /首頁/計算機/java語言/列表

Java程式設計示例教程

本文以例項形式詳細講述了Java的反射機制,是Java程式設計中重要的技巧。分享給大家供大家參考。具體分析如下:

Java程式設計示例教程

首先,Reflection是Java 程式開發語言的特徵之一,它允許執行中的 Java 程式對自身進行檢查,或者說"自審",並能直接操作程式的內部屬性。例如,使用它能獲得 Java 類中各成員的名稱並顯示出來。 Java 的這一能力在實際應用中也許用得不是很多,但是在其它的程式設計語言中根本就不存在這一特性。例如,Pascal、C 或者 C++ 中就沒有辦法在程式中獲得函式定義相關的資訊。

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

1. 一個簡單的例子

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

import ect.*; public class DumpMethods { public static void main(String args[]) { try { Class c = ame("k"); Method m[] = eclaredMethods(); for (int i = 0; i < th; i++) tln(m[i]ring()); } catch (Throwable e){ tln(e); } } }

它的結果輸出為:

public synchronized ct ()public ct (ct)public boolean y()public synchronized ct ()public synchronized int ch(ct)

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

這個程式使用 ame 載入指定的類,然後呼叫 getDeclaredMethods 來獲取這個類中定義了的方法列表。ods 是用來描述某個類中單個方法的一個類。

2.開始使用 Reflection

用於 reflection 的類,如 Method,可以在 ect 包中找到。使用這些類的時候必須要遵循三個步驟:第一步是獲得你想操作的類的 s 物件。在執行中的 Java 程式中,用 s 類來描述類和介面等。

下面就是獲得一個 Class 物件的方法之一:

Class c = ame("ng");

這條語句得到一個 String 類的類物件。還有另一種方法,如下面的語句:

Class c = s; 或者 Class c = ;

它們可獲得基本型別的類資訊。其中後一種方法中訪問的是基本型別的封裝類 (如 Integer) 中預先定義好的 TYPE 欄位。

第二步是呼叫諸如 getDeclaredMethods 的方法,以取得該類中定義的所有方法的列表。

一旦取得這個資訊,就可以進行第三步了——使用 reflection API 來操作這些資訊,如下面這段程式碼:

Class c = ame("ng"); Method m[] = eclaredMethods(); tln(m[0]ring());

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

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

模擬 instanceof 操作符

得到類資訊之後,通常下一個步驟就是解決關於 Class 物件的一些基本的問題。例如,stance 方法可以用於模擬 instanceof 操作符:

class S { } public class IsInstance { public static void main(String args[]) { try { Class cls = ame("S"); boolean b1 = stance(new Integer(37)); tln(b1); boolean b2 = stance(new S()); tln(b2); } catch (Throwable e) { tln(e); } } }

在這個例子中建立了一個S 類的 Class 物件,然後檢查一些物件是否是S的例項。Integer(37) 不是,但 new S()是。

3.找出類的方法

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

import ect.*; public class Method1 { private int f1(Object p, int x) throws NullPointerException { if (p == null) throw new NullPointerException(); return x; } public static void main(String args[]) { try { Class cls = ame("Method1"); Method methlist[] = eclaredMethods(); for (int i = 0; i < th; i++) { Method m = methlist[i]; tln("name = " + ame()); tln("decl class = " + eclaringClass()); Class pvec[] = arameterTypes(); for (int j = 0; j < th; j++) tln("param #" + j + " " + pvec[j]); Class evec[] = xceptionTypes(); for (int j = 0; j < th; j++) tln("exc #" + j + " " + evec[j]); tln("return type = " + eturnType()); tln("-----"); } } catch (Throwable e) { tln(e); } } }

這個程式首先取得 method1 類的描述,然後呼叫 getDeclaredMethods 來獲取一系列的 Method 物件,它們分別描述了定義在類中的每一個方法,包括 public 方法、protected 方法、package 方法和 private 方法等。如果你在程式中使用 getMethods 來代替 getDeclaredMethods,你還能獲得繼承來的各個方法的資訊。

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

輸出的結果如下:

name = f1 decl class = class method1 param #0 class ct param #1 int exc #0 class PointerException return type = int-----name = main decl class = class method1 param #0 class [ng; return type = void

4.獲取構造器資訊

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

import ect.*;public class Constructor1 { public Constructor1() { } protected Constructor1(int i, double d) { } public static void main(String args[]) { try { Class cls = ame("Constructor1"); Constructor ctorlist[] = eclaredConstructors(); for (int i = 0; i < th; i++) { Constructor ct = ctorlist[i]; tln("name = " + ame()); tln("decl class = " + eclaringClass()); Class pvec[] = arameterTypes(); for (int j = 0; j < th; j++) tln("param #" + j + " " + pvec[j]); Class evec[] = xceptionTypes(); for (int j = 0; j < th; j++) tln("exc #" + j + " " + evec[j]); tln("-----"); } } catch (Throwable e) { tln(e); } } }

這個例子中沒能獲得返回型別的相關資訊,那是因為構造器沒有返回型別。

這個程式執行的結果是:

name = Constructor1decl class = class Constructor1param #0 intparam #1 double-----name = Constructor1decl class = class Constructor1-----

5.獲取類的欄位(域)

找出一個類中定義了哪些資料欄位也是可能的.,下面的程式碼就在幹這個事情:

import ect.*; public class Field1 { private double d; public static final int i = 37; String s = "testing"; public static void main(String args[]) { try { Class cls = ame("Field1"); Field fieldlist[] = eclaredFields(); for (int i = 0; i < th; i++) { Field fld = fieldlist[i]; tln("name = " + ame()); tln("decl class = " + eclaringClass()); tln("type = " + ype()); int mod = odifiers(); tln("modifiers = " + ring(mod)); tln("-----"); } } catch (Throwable e) { tln(e); } } }

這個例子和前面那個例子非常相似。例中使用了一個新東西 Modifier,它也是一個 reflection 類,用來描述欄位成員的修飾語,如“private int”。這些修飾語自身由整數描述,而且使用 ring 來返回以“官方”順序排列的字串描述 (如“static”在“final”之前)。這個程式的輸出是:

name = ddecl class = class Field1type = doublemodifiers = private-----name = idecl class = class Field1type = intmodifiers = public static final-----name = sdecl class = class Field1type = class ngmodifiers = -----

和獲取方法的情況一下,獲取欄位的時候也可以只取得在當前類中申明瞭的欄位資訊 (getDeclaredFields),或者也可以取得父類中定義的欄位 (getFields) 。

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

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

import ect.*; public class Method2 { public int add(int a, int b) { return a + b; } public static void main(String args[]) { try { Class cls = ame("Method2"); Class partypes[] = new Class[2]; partypes[0] = ; partypes[1] = ; Method meth = ethod("add", partypes); Method2 methobj = new Method2(); Object arglist[] = new Object[2]; arglist[0] = new Integer(37); arglist[1] = new Integer(47); Object retobj = ke(methobj, arglist); Integer retval = (Integer) retobj; tln(alue()); } catch (Throwable e) { tln(e); } } }

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

上例中,getMethod用於查詢一個具有兩個整型引數且名為 add 的方法。找到該方法並建立了相應的Method 物件之後,在正確的物件例項中執行它。執行該方法的時候,需要提供一個引數列表,這在上例中是分別包裝了整數 37 和 47 的兩個 Integer 物件。執行方法的返回的同樣是一個 Integer 物件,它封裝了返回值 84。

7.建立新的物件

對於構造器,則不能像執行方法那樣進行,因為執行一個構造器就意味著建立了一個新的物件 (準確的說,建立一個物件的過程包括分配記憶體和構造物件)。所以,與上例最相似的例子如下:

import ect.*; public class Constructor2 { public Constructor2() { } public Constructor2(int a, int b) { tln("a = " + a + " b = " + b); } public static void main(String args[]) { try { Class cls = ame("Constructor2"); Class partypes[] = new Class[2]; partypes[0] = ; partypes[1] = ; Constructor ct = onstructor(partypes); Object arglist[] = new Object[2]; arglist[0] = new Integer(37); arglist[1] = new Integer(47); Object retobj = nstance(arglist); } catch (Throwable e) { tln(e); } } }

根據指定的引數型別找到相應的建構函式並執行它,以建立一個新的物件例項。使用這種方法可以在程式執行時動態地建立物件,而不是在編譯的時候建立物件,這一點非常有價值。

8.改變欄位(域)的值

reflection 的還有一個用處就是改變物件資料欄位的值。reflection 可以從正在執行的程式中根據名稱找到物件的欄位並改變它,下面的例子可以說明這一點:

import ect.*; public class Field2 { public double d; public static void main(String args[]) { try { Class cls = ame("Field2"); Field fld = ield("d"); Field2 f2obj = new Field2(); tln("d = " + f2obj.d); ouble(f2obj, 12.34); tln("d = " + f2obj.d); } catch (Throwable e) { tln(e); } } }

這個例子中,欄位 d 的值被變為了 12.34。

9.使用陣列

本文介紹的 reflection 的最後一種用法是建立的運算元組。陣列在 Java 語言中是一種特殊的類型別,一個數組的引用可以賦給 Object 引用。觀察下面的例子看看陣列是怎麼工作的:

import ect.*; public class Array1 { public static void main(String args[]) { try { Class cls = ame("ng"); Object arr = nstance(cls, 10); (arr, 5, "this is a test"); String s = (String) (arr, 5); tln(s); } catch (Throwable e) { tln(e); } } }

例中建立了 10 個單位長度的 String 陣列,為第 5 個位置的字串賦了值,最後將這個字串從陣列中取得並列印了出來。

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

import ect.*; public class Array2 { public static void main(String args[]) { int dims[] = new int[]{5, 10, 15}; Object arr = nstance(, dims); Object arrobj = (arr, 3); Class cls = lass()omponentType(); tln(cls); arrobj = (arrobj, 5); nt(arrobj, 10, 37); int arrcast[][][] = (int[][][]) arr; tln(arrcast[3][5][10]); } }

例中建立了一個 5 x 10 x 15 的整型陣列,併為處於 [3][5][10] 的元素賦了值為 37。注意,多維陣列實際上就是陣列的陣列,例如,第一個 之後,arrobj 是一個 10 x 15 的陣列。進而取得其中的一個元素,即長度為 15 的陣列,並使用 nt 為它的第 10 個元素賦值。

注意建立陣列時的型別是動態的,在編譯時並不知道其型別。

相信本文所述對大家Java程式設計的學習有一定的借鑑價值。

TAG標籤:示例 JAVA 程式設計 #