春節到了,雖然在放假,但是也總該做些事情,正好最近對java的反射比較感興趣,所以就學習了一下反射,順便做了翻譯,水平真的有限,但起碼算是學習后留下了一些值得回憶的東西,陸續刊登出來,希望大家多多指教。
反射API
反射API表現,或者反射類,接口,和對象在現有的java虛擬機。你將使用反射API如果你正在編寫開發工具例如調試器,類瀏覽器,和圖形用戶界面構建器,使用反射API你可以:
。决定對象的類。
。獲得有關類的修改器,域,方法,構建器和超類的信息。
。查找屬于接口的聲明方法或者常量。
。獲得直到運行時才明確的類的實例。
。獲得或者設置對象域的值,即使你的域的名字直到運行時才知道。
。調用對象的方法,即使方法直到運行時才知道。
。創建一個新的數組,這個數組的長度和組成類型直到運行時才知道,然後修改數組的組成。
首先,要注意的是。不要在有其他工具更加有效率的情況下使用反射API。例如,如果你習慣使用函數指針在其他語言中,你更習慣于使用反射API的Method對象。請抵御這樣的誘惑!你的程序將會更加容易調試和維護如果你不使用Method對象。換言之,你應該定義一個接口,然後實現這個接口。
其他的課程使用使用術語“成員變量”代替“域”,遮兩個術語是相同的。因爲Field類是反射API的一部分,這個課程使用術語“域”。
這個課程對于反射API使用面向任務的方法。每一個課程包含一個相關任務的集合,每一個任務會一步步解釋,并有實例程序。課程目錄如下:
。檢查類 解釋如何决定對象的類,如何獲得相關類和接口的信息。
。操縱對象 介紹如何實例化類,獲得或者設置域的值,如何調用方法。使用反射API,你可以執行這些任務即使你不知道這些類,域,方法的名字,直到運行時。
。操縱數組 描述使用API麳創建和修改數組,這些數組的名字直到運行時才知道。
。類的總結 列出反射API的組成類,提供API文檔的鏈接。
課程:檢查類
如果你正在編寫類的瀏覽器,你需要一個方式麳獲得運行時類的信息。例如,你想要顯示類的域,方法,構造函數的名字。或者,你想要顯示哪些接口被類實現。爲了獲得這個信息你需要獲得Class對象,這個對象反射這個類。
對于每一個類,java運行時環境維護一個不可變的Class對象這個對象包含有關這個類的信息。一個Class對象顯示或者反射這個類。使用反射API,你可以調用Class對象的方法,這個對象可以返回Constructor,Method,Field對象。你可以使用這些對象麳獲得有關定義在類中的相應的搆造函數,方法和域的信息。
Class對象也可以用于接口。你調用Class方法麳查找相關接口的修飾符,方法,和共有的常量。不是所有的Class方法在Class對象反射一個接口的時候都適用。例如,當Class對象反射一個接口的時候調用getConstructors是無意義的。檢查接口這一節將會解釋哪些Class的方法你可以使用麳獲得關于接口的信息。
獲得Class對象
第一件事情,在你查找有關類的任何事情之前,你必須首先獲得相應的Class對象。
你可以通過幾種方式獲得Class對象:
。如果可以獲得一個類的實例,你可以調用Object.getClass。getClass方法當你想檢查一個不知道類的對象的時候很有用。以下幾行代碼可以獲得名為mystery的Class對象:
Class c = mystery.getClass();
。如果你想獲得Class對象通過超類這個超類是其他Class對象映射的,調用getSuperclass方法。以下的實例,getSuperclass返回Class有關TextComponent類的Class對象,因爲TextComponent是一個TextField的超類:
TextField t = new TextField();
Class c = t.getClass();
Class s = c.getSuperclass();
。如果你想在編譯的時候知道類的名字,你可以返回Class對象通過在類的名字後面將上.class。在下麵的實例中,Button類返回以下Class對象:
Class c = java.awt.Button.class;
。如果在編譯的時候不知道類名,但是在運行時可以知道,你可以使用forName方法。在以下的實例中,如果String對象的實例strg設置到java.awt.Button,然後forName返回有關Button類的Class對象:
Class c = Class.forName(strg);
獲得類名
查找Class對象的名字是很容易的。你所需要做的就是調用getName方法。
在java語言中每一個類都有一個名字。當你聲明一個類后,這個名字跟隨在class關鍵詞後面。以下的類聲明,類名爲Point:
public class Point{int x,y;}
在運行時,你可以决定Class對象的名字,通過調用getName方法。getName方法返回的String對象是一個類的全路徑名。
以下的程序獲得一個對象的類名。第一,它返回相應的Class對象,然後它調用Class對象的getName方法。
import java.lang.reflect.*;//這句在這個程序裏面沒有用到
import java.awt.*;
class SampleName {
public static void main(String[] args) {
Button b = new Button();
printName(b);
}
static void printName(Object o) {
Class c = o.getClass();
String s = c.getName();
System.out.println(s);
}
}
示例程序打印出如下的語句:
java.awt.Button
發現類脩飾符
這一節展示的是你需要查找特定類的脩飾符所要調用的方法。
定義一個類需要以下的脩飾符:public,abstract,或者final。類脩飾符在class關鍵詞的前面。以下的例子中,類脩飾符是public和final:
public final class Coordinate {int x, int y, int z}
在運行時指定類的脩飾符你可以執行以下步驟:
1. 調用Class對象的getModifiers方法麳返回脩飾符的集合。
2. 通過調用isPublic,isAbstract和isFinal方法麳檢查脩飾符。
以下程序指定String類的脩飾符。
import java.lang.reflect.*;
import java.awt.*;
class SampleModifier {
public static void main(String[] args) {
String s = new String();
printModifiers(s);
}
public static void printModifiers(Object o) {
Class c = o.getClass();
int m = c.getModifiers();//m爲17,是16和1的合,public爲1,final爲16。
if (Modifier.isPublic(m))
System.out.println("public");
if (Modifier.isAbstract(m))
System.out.println("abstract");
if (Modifier.isFinal(m))
System.out.println("final");
}
}
示例程序的輸出:
public
final
尋找超類
這一節你將要學到如何返回指定類的祖先的所有Class對象。
因爲java語言支持繼承,一個例如類瀏覽器的應用程序必須可以指定父類。爲了决定類的父類,你可以調用getSuperclass方法。這個方法返回超類的Class對象,或者返回null如果這個類沒有超類。爲了指定所有的父類,可以迭代調用getSuperclass直到返回null爲止。
以下這個程序查找Button類的父類的名字。
import java.lang.reflect.*;
import java.awt.*;
class SampleSuper {
public static void main(String[] args) {
Button b = new Button();
printSuperclasses(b);
}
static void printSuperclasses(Object o) {
Class subclass = o.getClass();
Class superclass = subclass.getSuperclass();
while (superclass != null) {
String className = superclass.getName();
System.out.println(className);
subclass = superclass;
superclass = subclass.getSuperclass();
}
}
}
示例程序的輸出爲:
java.awt.Component
java.lang.Object
指定被類實現的接口
如果你想查找一個接口被哪個類實現,請看遮一節。
一個對象的類型不僅在它的類合超類被决定,而且可以被它的接口。在類聲明中,接口在implements關鍵詞後面被列出。例如RandomAccessFile類實現DataOutput合DataInput接口:
public class RandomAccessFile implements DataOutput,DataInput
你調用getInterfaces方法麳决定哪個類實現了接口。getInterfaces方法返回一個Class對象的數組。反射API顯示了Class對象。每一個Class對象在數組中通過getInterfaces返回被這個類實現的接口。你可以調用getName方法返回接口的名字。更多信息,請看檢查接口。
以下程序打印RandomAccessFile實現的接口。
import java.lang.reflect.*;
import java.io.*;
class SampleInterface {
public static void main(String[] args) {
try {
RandomAccessFile r = new RandomAccessFile("myfile", "r");
printInterfaceNames(r);
} catch (IOException e) {
System.out.println(e);
}
}
static void printInterfaceNames(Object o) {
Class c = o.getClass();
Class[] theInterfaces = c.getInterfaces();
for (int i = 0; i < theInterfaces.length; i++) {
String interfaceName = theInterfaces[i].getName();
System.out.println(interfaceName);
}
}
}
打印出以下信息:
java.io.DataOutput
java.io.DataInput
檢查接口
這一節你將要學到如果一個Class對象如何現死成一個接口或者一個類。你將獲得一些竅門,如何獲得有關接口的更多信息。
Class對象顯示接口与類一樣。如果你不確定一個Class對象到底是接口還是類,調用isInterface方法。
你調用Class方法麳獲得有關接口的信息。爲了查找共有的常量,調用Class對象的getFields方法。指定類的域這一節有一個示例包含getFields。你可以使用getMethods麳獲得有關接口方法的更多信息。參見獲得方法的信息這一節。查找有關接口的脩飾符,調用getModifiers方法。參見發現類脩飾符的例子。
import java.lang.reflect.*;
import java.util.*;
class SampleCheckInterface {
public static void main(String[] args) {
Class observer = Observer.class;
Class observable = Observable.class;
verifyInterface(observer);
verifyInterface(observable);
}
static void verifyInterface(Class c) {
String name = c.getName();
if (c.isInterface()) {
System.out.println(name + " is an interface.");
} else {
System.out.println(name + " is a class.");
}
}
}
輸出如下:
java.util.Observer is an interface。
Java.util.Observable is a class。
指定類的域
這一節描述如何發現屬于類的域,如何發現這些域的更多信息。
如果你正在編寫一個類瀏覽器的應用程序,你可能想要發現屬于一個特定類的域。你可以指定類的域通過getFields方法。getFields方法返回一個Field對象的數組包含每一個可訪問的public域。
一個共有的域可以被任何一個成員訪問:
這個類
這個類的超類
實現這個接口的類
這個類實現的接口的接口
這個方法提供Field類允許你返回域的名字,類型合脩飾符的結婚。你可以獲得或者設置域的值,這些在獲得域的值合設置域的值中會有描述。
以下的程序打印名字合類型屬于GridBagConstraints類。注意這個程序首先返回Field對象,然後調用getName合getType方法。
import java.lang.reflect.*;
import java.awt.*;
class SampleField {
public static void main(String[] args) {
GridBagConstraints g = new GridBagConstraints();
printFieldNames(g);
}
static void printFieldNames(Object o) {
Class c = o.getClass();
Field[] publicFields = c.getFields();
for (int i = 0; i < publicFields.length; i++) {
String fieldName = publicFields[i].getName();
Class typeClass = publicFields[i].getType();
String fieldType = typeClass.getName();
System.out.println("Name: " + fieldName +
", Type: " + fieldType);
}
}
}
輸出如下:
Name: RELATIVE, Type: int
Name: REMAINDER, Type: int
Name: NONE, Type: int
Name: BOTH, Type: int
Name: HORIZONTAL, Type: int
Name: VERTICAL, Type: int
發現類的搆造函數
這一節介紹Constructor類,解釋如何獲得有關類的搆造函數的信息。
爲了創建一個類的實例,你調用一個特殊的方法,這個方法叫做構造函數。象方法一樣,構造函數可以被重載。
你可以獲得有關類的構造函數的信息,通過調用getConstructors方法,這個方法返回一個Constructor對象的數組。你可以使用Constructor類提供的方法麳决定構造函數的名字,脩飾符的結合,參數類型,合拋出异常的集合。你可以創建一個Constructor對象的實例,通過Constructor.newInstance犯法。你將會雪到如何調用Constructor.newInstance方法在操縱對象這一節。
以下示例程序打印每一個構造函數的參數類型。程序按以下步驟執行:
1. 它返回一個Constructor對象的數組通過調用getConstructors。
2. 在Constructor數組的每一個元素,它創建一個Class對象的數組通過調用getParameterTypes。Class對象在數組中顯示搆造函數的參數。
3. 程序調用getName麳獲得類名。
听起來幷不複雜。以下是源代碼:
import java.lang.reflect.*;
import java.awt.*;
class SampleConstructor {
public static void main(String[] args) {
Rectangle r = new Rectangle();
showConstructors(r);
}
static void showConstructors(Object o) {
Class c = o.getClass();
Constructor[] theConstructors = c.getConstructors();
for (int i = 0; i < theConstructors.length; i++) {
System.out.print("( ");
Class[] parameterTypes =
theConstructors[i].getParameterTypes();
for (int k = 0; k < parameterTypes.length; k ++) {
String parameterString = parameterTypes[k].getName();
System.out.print(parameterString + " ");
}
System.out.println(")");
}
}
}
第一行輸出沒有參數出現因爲特定的Constructor對象顯示一個沒有參數的搆造函數。在以下的行。
( )
( int int )
( int int int int )
( java.awt.Dimension )
( java.awt.Point )
( java.awt.Point java.awt.Dimension )
( java.awt.Rectangle )
獲得方法信息
爲了發現有關類的方法,你需要返回相應的Method對象。這一節將展示如何做到這點。
爲了發現屬于類的共有方法,調用getMethods方法。返回包含Method對象的數組。你可以使用Method對象麳獲得方法的名字,返回類型,參數類型,脩飾符集合,拋出异常的類型。所有這些信息在你編寫一個類的瀏覽器或者一個調試器的時候都是很有用的。通過Method.invoke,你可以調用方法自身。想瞭解如何做到這點,參見調用方法這一節。
以下的示例程序打印名字,返回類型,每一個共有方法的參數類型。程序執行如下的任務:
1. 返回一個Method對象的數組,通過調用getMethods方法。
2. 遍曆每一個元素:
a) 返回方法的名字通過調用getName方法。
b) 獲得返回類型通過調用getReturnType。
c) 創建一個Class對象的數組通過調用getParameterTypes方法。
3. Class對象的數組顯示方法的參數。爲了返回每一個參數的類名,程序調用getName方法。
完成這個任務不需要太多的代碼:
import java.lang.reflect.*;
import java.awt.*;
class SampleMethod {
public static void main(String[] args) {
Polygon p = new Polygon();
showMethods(p);
}
static void showMethods(Object o) {
Class c = o.getClass();
Method[] theMethods = c.getMethods();
for (int i = 0; i < theMethods.length; i++) {
String methodString = theMethods[i].getName();
System.out.println("Name: " + methodString);
String returnString =
theMethods[i].getReturnType().getName();
System.out.println(" Return Type: " + returnString);
Class[] parameterTypes = theMethods[i].getParameterTypes();
System.out.print(" Parameter Types:");
for (int k = 0; k < parameterTypes.length; k ++) {
String parameterString = parameterTypes[k].getName();
System.out.print(" " + parameterString);
}
System.out.println();
}
}
}
輸出如下:
Name: equals
Return Type: boolean
Parameter Types: java.lang.Object
Name: getClass
Return Type: java.lang.Class
Parameter Types:
Name: hashCode
Return Type: int
Parameter Types:
.
.
.
Name: intersects
Return Type: boolean
Parameter Types: double double double double
Name: intersects
Return Type: boolean
Parameter Types: java.awt.geom.Rectangle2D
Name: translate
Return Type: void
Parameter Types: int int