前言
本文是我整理的java反射的一些知識(shí),其中大部分內(nèi)容是翻譯http://tutorials.jenkov.com/java-reflection/index.html的。
JAVA反射機(jī)制是在運(yùn)行狀態(tài)中,對(duì)于任意一個(gè)類,都能夠知道這個(gè)類的所有屬性和方法;對(duì)于任意一個(gè)對(duì)象,都能夠調(diào)用它的任意一個(gè)方法;這種動(dòng)態(tài)獲取的信息以及動(dòng)態(tài)調(diào)用對(duì)象的方法的功能稱為Java語(yǔ)言的反射機(jī)制。
Java反射機(jī)制是Java語(yǔ)言被視為“準(zhǔn)動(dòng)態(tài)”語(yǔ)言的關(guān)鍵性質(zhì)。Java反射機(jī)制的核心就是允許在運(yùn)行時(shí)通過(guò)Java Reflection APIs來(lái)取得已知名字的class類的內(nèi)部信息(包括其modifiers(諸如public, static等等)、superclass(例如Object)、實(shí)現(xiàn)interfaces(例如Serializable),也包括fields和methods的所有信息),動(dòng)態(tài)地生成此類,并調(diào)用其方法或修改其域(甚至是本身聲明為PRivate的域或方法)。
Java反射機(jī)制主要提供了以下功能:在運(yùn)行時(shí)判斷任意一個(gè)對(duì)象所屬的類;在運(yùn)行時(shí)構(gòu)造任意一個(gè)類的對(duì)象;在運(yùn)行時(shí)判斷任意一個(gè)類所具有的成員變量和方法;在運(yùn)行時(shí)調(diào)用任意一個(gè)對(duì)象的方法;生成動(dòng)態(tài)代理。
Class對(duì)象是Java反射的基礎(chǔ),它包含了與類相關(guān)的信息,事實(shí)上,Class對(duì)象就是用來(lái)創(chuàng)建類的所有對(duì)象的。Class對(duì)象是java.lang.Class<T>這個(gè)類生成的對(duì)象,其中類型參數(shù)T表示由此 Class 對(duì)象建模的類的類型。例如,String.class的類型是 Class<String>;如果將被建模的類未知,則使用Class<?>。以下是Java API的描述:
Class類的實(shí)例表示正在運(yùn)行的 Java應(yīng)用程序中的類和接口。枚舉是一種類,注釋是一種接口。每個(gè)數(shù)組屬于被映射為Class對(duì)象的一個(gè)類,所有具有相同元素類型和維數(shù)的數(shù)組都共享該Class對(duì)象。基本的Java類型(boolean、byte、char、short、int、long、float和double)和關(guān)鍵字void也表示為Class對(duì)象。
Class沒有公共構(gòu)造方法。Class對(duì)象是在加載類時(shí)由Java虛擬機(jī)以及通過(guò)調(diào)用類加載器中的defineClass方法自動(dòng)構(gòu)造的。
實(shí)際上,每個(gè)類都有一個(gè)Class對(duì)象。換言之,每當(dāng)編寫并且編譯了一個(gè)新類,就會(huì)產(chǎn)生一個(gè)Class對(duì)象(更恰當(dāng)?shù)恼f(shuō),是被保存在一個(gè)同名的.class文件中)。如果我們想生成這個(gè)類的對(duì)象,運(yùn)行這個(gè)程序的Java虛擬機(jī)(JVM)將使用類加載器子系統(tǒng),類加載器首先檢查這個(gè)類的Class對(duì)象是否已經(jīng)加載。如果尚未加載,默認(rèn)的類加載器就會(huì)根據(jù)類名查找.class文件,并將其載入,一旦某個(gè)類的Class對(duì)象被載入內(nèi)存,它就被用來(lái)創(chuàng)建這個(gè)類的所有對(duì)象。
獲取Class對(duì)象有三種方式:
(1) 通過(guò)實(shí)例變量的getClass()方法。例如:
Class c1 = new String("abc").getClass();
(2) 通過(guò)Class類的靜態(tài)方法——forName()來(lái)實(shí)現(xiàn),例如:
Class class =Class.forName(className);
注意:當(dāng)使用Class.forName()方法時(shí),你必須提供完全限定類名。即類名要包括所有包
名。例如,如果MyObject是位于包c(diǎn)om.jenkov.myapp下,那么類的完全限定名稱是com.jenkov.myapp.MyObject。如果在運(yùn)行時(shí)類路徑上找不到類,Class.forName()方法會(huì)拋出一個(gè)ClassNotFoundException。
(3) 使用類字面常量或TYPE字段,例如:
Class myObjectClass= MyObject.class;(類字面常量不僅可以應(yīng)用于普通的類,也可以應(yīng)用
于接口、數(shù)組以及基本數(shù)據(jù)類型),這種方式不僅更簡(jiǎn)單,而且更安全,因?yàn)樗诰幾g時(shí)就會(huì)受到檢查,并且根除了對(duì)forName方法的調(diào)用,所以也更高效,建議使用“.class”的形式。
Class c = Integer.TYPE;(TYPE是基本數(shù)據(jù)類型的包裝類型的一個(gè)標(biāo)準(zhǔn)字段,它是一
個(gè)引用,指向?qū)?yīng)的基本數(shù)據(jù)類型的Class對(duì)象),附表如下,兩邊等價(jià):
boolean.class | Boolean.TYPE |
char.class | Character.TYPE |
byte.class | Byte.TYPE |
short.class | Short.TYPE |
int.class | Integer.TYPE |
long.class | Long.TYPE |
float.class | Float.TYPE |
double.class | Double.TYPE |
void.class | Void.TYPE |
使用Java反射,你可以在運(yùn)行時(shí)檢查Java類。檢查類是使用反射時(shí)經(jīng)常做的第一件事情。從類中可以獲取以下信息:
(1) 類名
(2) 類修飾符 (public, private, synchronized等)
(3) 包信息
(4) 父類
(5) 實(shí)現(xiàn)的接口
(6) 構(gòu)造函數(shù)
(7) 方法
(8) 字段
(9) 注解
從Class對(duì)象中可以獲取兩個(gè)不同的類名。完全限定類名(包括包名)可以使用getName()或getCanonicalName()方法獲取,例如:
Class aClass = MyObject.class;String className = aClass.getName();String className1 = aClass.getCanonicalName();如果想要獲取不含包名的類名可以使用
getSimpleName()方法,如下:Class aClass = MyObject.class;String simpleClassName = aClass.getSimpleName();3.2修飾符
使用Class對(duì)象可以獲取一個(gè)類的修飾符.類的修飾符即關(guān)鍵字"public","private", "static"等. 如下:
Class aClass = MyObject.class;int modifiers = aClass.getModifiers();修飾符被包裝進(jìn)一個(gè)int
內(nèi),每一個(gè)修飾符都是一個(gè)標(biāo)志位(置位或清零)。可以使用java.lang.reflect.Modifier類中的以下方法來(lái)檢驗(yàn)修飾符:Modifier.isAbstract(int modifiers)Modifier.isFinal(int modifiers)Modifier.isInterface(int modifiers)Modifier.isNative(int modifiers)Modifier.isPrivate(int modifiers)Modifier.isProtected(int modifiers)Modifier.isPublic(int modifiers)Modifier.isStatic(int modifiers)Modifier.isStrict(int modifiers)Modifier.isSynchronized(int modifiers)Modifier.isTransient(int modifiers)Modifier.isVolatile(int modifiers)3.3包信息
使用Class對(duì)象可以獲取包信息,如下:
Class aClass = MyObject.class;Package package = aClass.getPackage();String packageName = package.getname();從Package對(duì)象中你可以訪問諸如名字等包信息。您還可以訪問類路徑上這個(gè)包位于JAR文件中Manifest這個(gè)文件中指定的信息。例如,你可以在Manifest文件中指定包的版本號(hào)。可以在java.lang.Package中了解更多包類信息。
3.4父類
通過(guò)
Class對(duì)象可以獲取類的父類,如下:Class aClass = MyObject.class;Class superclass = aClass.getSuperclass();父類的Class對(duì)象和其它Class對(duì)象一樣是一個(gè)Class對(duì)象,可以繼續(xù)使用反射.
3.5實(shí)現(xiàn)的接口
通過(guò)給定的類可以獲取這個(gè)類所實(shí)現(xiàn)的接口列表,如下:
Class aClass = MyObject.class;Class[] interfaces = aClass.getInterfaces();一個(gè)類可以實(shí)現(xiàn)多個(gè)接口。因此返回一個(gè)Class數(shù)組。在Java反射機(jī)制中,接口也由Class對(duì)象表示。
注意:只有給定類聲明實(shí)現(xiàn)的接口才會(huì)返回。例如,如果類A的父類B實(shí)現(xiàn)了一個(gè)接口C,但類A并沒有聲明它也實(shí)現(xiàn)了C,那么C不會(huì)被返回到數(shù)組中。即使類A實(shí)際上實(shí)現(xiàn)了接口C,因?yàn)樗母割怋實(shí)現(xiàn)了C。
為了得到一個(gè)給定的類實(shí)現(xiàn)接口的完整列表,需要遞歸訪問類和其超類。
3.6構(gòu)造函數(shù)
使用Class對(duì)象可以獲取類的構(gòu)造函數(shù),如下:
Class aClass = MyObject.class;Constructor[] constructors = aClass.getConstructors();關(guān)于構(gòu)造函數(shù)更詳細(xì)信息參見 構(gòu)造函數(shù)這節(jié)。
3.7方法
使用Class對(duì)象可以獲取類的方法,如下:
Class aClass = MyObject.class;Method[] methods = aClass.getMethods();關(guān)于方法更詳細(xì)信息參見方法這節(jié).
3.8字段
使用Class對(duì)象可以獲取類的字段(成員變量),如下:
Class aClass = MyObject.class;Field[] fields = aClass.getFields();關(guān)于字段更詳細(xì)信息參見 字段這節(jié).
3.9注解
使用Class對(duì)象可以獲取類的注解,如下:
Class aClass = MyObject.class;Annotation[] annotations = aClass.getAnnotations();關(guān)于注解更詳細(xì)信息參見注解這節(jié).
4. 構(gòu)造函數(shù)
使用Java反射可以在運(yùn)行時(shí)檢查類的構(gòu)造函數(shù)并實(shí)例化對(duì)象。這是通過(guò)Java類java.lang.reflect.Constructor來(lái)實(shí)現(xiàn)的。以下是Java Construcor對(duì)象的更多細(xì)節(jié):
(1) 獲取Constructor對(duì)象
(2) 構(gòu)造函數(shù)參數(shù)
(3) 使用Constructor對(duì)象實(shí)例化對(duì)象
4.1獲取Constructor對(duì)象
Constructor類是從Class對(duì)象獲取的,舉例:Class aClass = MyObject.class;Constructor[] constructors = aClass.getConstructors();
Constructor數(shù)組為每一個(gè)在類中聲明的public構(gòu)造函數(shù)保存一個(gè)Constructor實(shí)例。如果知道要訪問的構(gòu)造函數(shù)確切的參數(shù)類型,可以不獲取構(gòu)造函數(shù)數(shù)組。本示例將返回給定類中接受一個(gè)字符串作為參數(shù)的公共構(gòu)造函數(shù)。
Class aClass = MyObject.class;//MyObject有一個(gè)參數(shù)為字符串的公共構(gòu)造函數(shù)Constructor constructor = aClass.getConstructor(new Class[]{String.class});如果沒有匹配給定的構(gòu)造函數(shù)參數(shù),在這個(gè)例子當(dāng)中是
String.class,會(huì)拋出NoSuchMethodException異常.4.2構(gòu)造函數(shù)參數(shù)
可以知道給定的構(gòu)造函數(shù)接受什么參數(shù),如下:
Class aClass = MyObject.class;//MyObject有一個(gè)參數(shù)為字符串的公共構(gòu)造函數(shù)Constructor constructor = aClass.getConstructor(new Class[]{String.class});Class[] parameterTypes = constructor.getParameterTypes();4.3使用Constructor對(duì)象實(shí)例化對(duì)象
可以像這樣實(shí)例化對(duì)象:
//獲取使用字符串作為參數(shù)的constructorConstructor constructor = MyObject.class.getConstructor(String.class);MyObject myObject = (MyObject)constructor.newInstance("constructor-arg1");
Constructor.newInstance()方法使用可變長(zhǎng)度的參數(shù),但是在調(diào)用構(gòu)造函數(shù)時(shí)必須為每一個(gè)參數(shù)提供一個(gè)準(zhǔn)確的參量.在這個(gè)例子中構(gòu)造函數(shù)接受一個(gè)字符串作為參數(shù),所以必須要提供一個(gè)字符串。5. 字段
使用Java反射你可以在運(yùn)行時(shí)檢查類的字段(成員變量)并 get / set它們.這是通過(guò)Java類
java.lang.reflect.Field來(lái)完成的.以下是JavaField對(duì)象更多細(xì)節(jié):(1) 獲取Field 對(duì)象
(2) 字段名稱
(3) 字段類型
(4) 獲取和設(shè)置字段值
5.1獲取Field 對(duì)象
Field類是從Class對(duì)象獲取的.舉例:Class aClass = MyObject.class;Field[] methods = aClass.getFields();
Field數(shù)組為類中聲明的每一個(gè)public字段保存一個(gè)Field實(shí)例。如果知道要訪問的字段名稱,可以這樣獲取它:
Class aClass = MyObject.classField field = aClass.getField("someField");根據(jù)下面
MyObject聲明的someField字段,以上的例子將返回Field實(shí)例:public class MyObject{public String someField = null;}如果不存在getField()方法中所給參數(shù)名字對(duì)應(yīng)的字段,會(huì)拋出
NoSuchFieldException異常.5.2字段名稱
一旦獲得一個(gè)
Field實(shí)例,可以使用Field.getName()方法獲取它的字段名字,如下:Field field = ... //獲取 field 對(duì)象String fieldName = field.getName();5.3字段類型
你可以使用
Field.getType()方法確定一個(gè)字段的類型(String,int等):Field field = aClass.getField("someField");Object fieldType = field.getType();5.4獲取和設(shè)置字段值
一旦獲得一個(gè)Field引用,可以使用
Field.get()和Field.set()方法獲取和設(shè)置它的值,如下:Class aClass = MyObject.classField field = aClass.getField("someField");MyObject objectInstance = new MyObject();Object value = field.get(objectInstance);field.set(objetInstance, value);傳遞給get和set方法的對(duì)象實(shí)例參數(shù)應(yīng)該是擁有該字段的類的一個(gè)實(shí)例。在上述的例子中使用了MyObject的一個(gè)實(shí)例,因?yàn)閟omeField是MyObject類的一個(gè)實(shí)例成員。靜態(tài)字段(public static)給get和set方法傳遞null作為參數(shù),而不是以上傳遞的objectInstance參數(shù)。
6. 方法
使用Java反射你可以在運(yùn)行時(shí)檢查類的方法并調(diào)用它們.這是通過(guò)Java類
java.lang.reflect.Method來(lái)實(shí)現(xiàn)的.本章將會(huì)更詳細(xì)的介紹 JavaMethod對(duì)象.下面是本章所涵蓋的主題:(1) 獲取 Method對(duì)象
(2) 方法的參數(shù)和返回值類型
(3) 使用Method對(duì)象調(diào)用方法
6.1獲取Method對(duì)象
Method類是從Class對(duì)象中獲取的.舉例:Class aClass = MyObject.class;Method[] methods = aClass.getMethods();
Method數(shù)組將為類中聲明的每一個(gè)public的方法保存一個(gè)Method實(shí)例.如果你知道要訪問方法的確切的參數(shù)類型,可以不必獲取方法數(shù)組。本示例返回給定類中接受一個(gè)字符串作為參數(shù)的公共方法”doSomething”。
Class aClass = MyObject.class;Method method = aClass.getMethod("doSomething", new Class[]{String.class});如果沒有方法匹配所給的方法名和參數(shù),在這個(gè)例子中是
String.class,將拋出NoSuchMethodException異常.如果你想訪問的方法沒有參數(shù),傳遞
null作為參數(shù)類型數(shù)組,如下:Class aClass = MyObject.class;Method method = aClass.getMethod("doSomething", null);6.2方法的參數(shù)和返回值類型
使用Method對(duì)象可以獲取方法的參數(shù),如下:
Method method = ... //獲取 method – 如上Class[] parameterTypes = method.getParameterTypes();亦可以獲取方法的返回值類型,如下:
Method method = ... // obtain method - see aboveClass returnType = method.getReturnType();6.3使用Method對(duì)象調(diào)用方法
可以像這樣調(diào)用方法:
//get method that takes a String as argumentMethod method = MyObject.class.getMethod("doSomething", String.class);Object returnValue = method.invoke(null, "parameter-value1");空參數(shù)是你想要調(diào)用該方法的對(duì)象。如果該方法是靜態(tài)的,使用null,而不是一個(gè)對(duì)象實(shí)例。在這個(gè)例子中,如果doSomething(String.class)不是靜態(tài)的,你需要提供有效的MyObject實(shí)例而不是null作為參數(shù);
Method.invoke(Object target, Object ...parameters)方法接受可變長(zhǎng)度的參數(shù),但是你在調(diào)用時(shí)必須為每一個(gè)參數(shù)提供一個(gè)準(zhǔn)確的參量。在這個(gè)例子中,方法以字符串作為參數(shù)的,所以必須提供一個(gè)字符串。7. get和set方法
使用Java反射可以在運(yùn)行時(shí)檢查類的方法并調(diào)用它們。這可以用來(lái)檢測(cè)一個(gè)給定的類有哪些get和set方法。可以通過(guò)掃描一個(gè)類的所有方法并檢查每個(gè)方法是否是get或set方法。
下面是一段用來(lái)找到類的get和set方法的代碼:
public staticvoid printGetterssetters(Class aClass){
Method[]methods = aClass.getMethods();
for(Methodmethod : methods){
if(isGetter(method))System.out.println("getter: " + method);
if(isSetter(method))System.out.println("setter: " + method);
}
}
public staticboolean isGetter(Method method){
if(!method.getName().startsWith("get")) return false;
if(method.getParameterTypes().length!= 0) return false;
if(void.class.equals(method.getReturnType())return false;
returntrue;
}
public staticboolean isSetter(Method method){
if(!method.getName().startsWith("set"))return false;
if(method.getParameterTypes().length!= 1) return false;
return true;
}
8. 私有字段和方法
可以通過(guò)Java反射訪問類的私有字段和方法。
8.1訪問私有字段
要想訪問私有字段你需要調(diào)用
Class.getDeclaredField(Stringname)或Class.getDeclaredFields()方法.Class.getField(String name)和Class.getFields()方法僅返回public字段。下面是一個(gè)簡(jiǎn)單的示例,類有一個(gè)私有字段,代碼通過(guò)反射來(lái)訪問這個(gè)私有字段:public class PrivateObject {private String privateString = null;public PrivateObject(String privateString) {this.privateString = privateString;}}PrivateObject privateObject = new PrivateObject("The Private Value");Field privateStringField = PrivateObject.class. getDeclaredField("privateString");privateStringField.setaccessible(true);String fieldValue = (String) privateStringField.get(privateObject);System.out.println("fieldValue = " + fieldValue);代碼示例輸出"fieldValue = The Private Value",即代碼示例最開始創(chuàng)建的
PrivateObject實(shí)例的私有字段privateString的值。注意
PrivateObject.class.getDeclaredField("privateString")的使用。這個(gè)方法僅僅返回特定類聲明的字段,而不包括任何父類中聲明的字段。注意粗體代碼.通過(guò)調(diào)用Field.setAcessible(true)方法關(guān)閉了特定Field實(shí)例的訪問檢查,現(xiàn)在通過(guò)反射可以訪問它,即使它是私有的,保護(hù)的或包范圍,甚至調(diào)用者不屬于這些范圍。但編譯器不允許使用普通的代碼該字段,因?yàn)閮H適用于反射。
8.2訪問私有方法
想要訪問私有方法需要調(diào)用
Class.getDeclaredMethod(String name,Class[] parameterTypes)或Class.getDeclaredMethods()方法.Class.getMethod(String name, Class[]parameterTypes)和Class.getMethods()方法僅返回public方法。下面是一個(gè)簡(jiǎn)單的示例,類有一個(gè)私有方法,代碼通過(guò)反射來(lái)訪問這個(gè)私有方法:public class PrivateObject {private String privateString = null;public PrivateObject(String privateString) {this.privateString = privateString;}private String getPrivateString(){return this.privateString;}}PrivateObject privateObject = new PrivateObject("The Private Value");Method privateStringMethod = PrivateObject.class.getDeclaredMethod("getPrivateString", null);privateStringMethod.setAccessible(true);String returnValue = (String)privateStringMethod.invoke(privateObject, null);System.out.println("returnValue = " + returnValue);代碼示例輸出" returnValue = The Private Value ",即代碼示例最開始創(chuàng)建的
PrivateObject實(shí)例調(diào)用的私有方法getPrivateString()的返回值。注意
PrivateObject.class.getDeclaredMethod("privateString")方法的使用。這個(gè)方法僅僅返回特定類聲明的方法,而不包括任何父類中聲明的方法。注意粗體代碼.通過(guò)調(diào)用
Method.setAcessible(true)方法關(guān)閉了特定Method實(shí)例的訪問檢查。現(xiàn)在通過(guò)反射可以訪問它,即使它是私有的,保護(hù)的或包范圍,甚至調(diào)用者不屬于這些范圍。但編譯器不允許使用普通的代碼訪問該方法,因?yàn)閮H適用于反射。9. Array
在Java反射中使用數(shù)組有時(shí)候有點(diǎn)棘手,特別是如果你想獲得某種類型的數(shù)組的Class對(duì)象,如int[]等。本節(jié)將討論如何通過(guò)Java反射創(chuàng)建數(shù)組和Class對(duì)象。以下是本章涵蓋的主題:
(1) java.lang.reflect.Array
(2) 創(chuàng)建數(shù)組
(3) 訪問數(shù)組
(4) 獲取數(shù)組的Class對(duì)象
(5) 獲取數(shù)組的組件類型
9.1 java.lang.reflect.Array
通過(guò)Java反射機(jī)制使用數(shù)組是由thejava.lang.reflect.Array類完成的。不要把這個(gè)類和Java集合框架中的java.util.Arrays類相混淆,java.util.Arrays類包含數(shù)組排序,將它們轉(zhuǎn)換為集合等公共方法。
9.2 創(chuàng)建數(shù)組
通過(guò)Java反射機(jī)制創(chuàng)建數(shù)組是由
java.lang.reflect.Array類來(lái)完成的,如下:int[] intArray = (int[]) Array.newInstance(int.class, 3);這行代碼創(chuàng)建了一個(gè)int數(shù)組。
Array.newInstance()方法的第一個(gè)參數(shù)告訴我們數(shù)組中的元素類型。第二個(gè)參數(shù)聲明了數(shù)組需要為多少個(gè)元素分配空間。9.3 訪問數(shù)組
使用Java反射機(jī)制可以訪問數(shù)組的元素。這是通過(guò)
Array.get(...)和Array.set(...)方法實(shí)現(xiàn)的,如下:int[] intArray = (int[]) Array.newInstance(int.class, 3);Array.set(intArray, 0, 123);Array.set(intArray, 1, 456);Array.set(intArray, 2, 789);System.out.println("intArray[0] = " + Array.get(intArray, 0));System.out.println("intArray[1] = " + Array.get(intArray, 1));System.out.println("intArray[2] = " + Array.get(intArray, 2));這段代碼輸出:
intArray[0] = 123intArray[1] = 456intArray[2] = 7899.4 獲取數(shù)組的Class對(duì)象
獲取數(shù)組的Class對(duì)象可以使用無(wú)反射的代碼,如下:
Class stringArrayClass = String[].class;使用
Class.forName()方法并不易懂。例如,你可以像下面這樣獲取int數(shù)組的Class對(duì)象:Class intArray = Class.forName("[I");字母
I在JVM中代表一個(gè)。左邊的[表示它是一個(gè)int數(shù)組的class。這對(duì)其它的基本類型也有效。對(duì)于對(duì)象,你需要使用一個(gè)稍微不同的記號(hào):
Class stringArrayClass = Class.forName("[Ljava.lang.String;");注意到左邊的”
[L”和右邊的”;”之間的類名。它代表對(duì)象數(shù)組的給定類型。另外一個(gè)需要注意的是你不能夠使用
Class.forName()方法獲取基本類型的Class對(duì)象。下面兩個(gè)例子都會(huì)拋出ClassNotFoundException異常:Class intClass1 = Class.forName("I");Class intClass2 = Class.forName("int");可以像下面這樣獲取基本數(shù)據(jù)類型和對(duì)象的Class名:
public Class getClass(String className){if("int" .equals(className)) return int .class;if("long".equals(className)) return long.class;...return Class.forName(className);}一旦你獲得某個(gè)類型的Class對(duì)象,有一個(gè)簡(jiǎn)單的方法來(lái)獲得該類型的數(shù)組的Class對(duì)象。解決方案是創(chuàng)建所需類型的空數(shù)組并從這個(gè)空數(shù)組獲取Class對(duì)象,如下:
Class theClass = getClass(theClassName);Class stringArrayClass = Array.newInstance(theClass, 0).getClass();這提供了一個(gè)單一的、統(tǒng)一的方法來(lái)訪問任何類型的數(shù)組的Class對(duì)象。
要確保
Class對(duì)象是一個(gè)數(shù)組,可以使用Class.isArray()方法來(lái)校驗(yàn):Class stringArrayClass = Array.newInstance(String.class, 0).getClass();System.out.println("is array: " + stringArrayClass.isArray());9.5 獲取數(shù)組的組件類型
一旦獲取到某個(gè)數(shù)組的Class對(duì)象可以使用
Class.getComponentType()方法來(lái)獲取它的組件類型。組件類型就是數(shù)組中元素的類型。例如,int數(shù)組的組件類型是int.class Class對(duì)象.String[]數(shù)組的組件類型是java.lang.StringClass對(duì)象。下面是獲取數(shù)組的組件類型的示例:
String[] strings = new String[3];Class stringArrayClass = strings.getClass();Class stringArrayComponentType = stringArrayClass.getComponentType();System.out.println(stringArrayComponentType);輸出 "class java.lang.String"即為String數(shù)組的組件類型.
10. 注解
使用Java反射你可以在運(yùn)行時(shí)訪問Java類中的注解.下面是本章涵蓋的主題:
(1)什么是Java注解?
(2)類注解
(3)方法注解
(4)參數(shù)注解
(5)字段注解
10.1 什么是Java注解?
注解是Java5的一個(gè)新功能。注解是一種可以在Java代碼中插入的注釋或元數(shù)據(jù)。這些注解可以在編譯時(shí)由預(yù)編譯工具進(jìn)行處理,也可以在運(yùn)行時(shí)通過(guò)Java反射機(jī)制來(lái)處理。下面是一個(gè)類注解的示例:
@MyAnnotation(name="someName", value = "Hello World")public class TheClass {}
TheClass類的上面有@MyAnnotation注解.注解像接口那樣定義。下面是MyAnnotation注解的定義:@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)public @interface MyAnnotation {public String name();public String value();}在
interface前面的”@”標(biāo)志它是一個(gè)注解。一旦你定義了那個(gè)注解你就可以在代碼中使用它,就像上面的例子那樣。在注解中定義的兩個(gè)指令
@Retention(RetentionPolicy.RUNTIME)和@Target(ElementType.TYPE)表明注解是如何使用的
@Retention(RetentionPolicy.RUNTIME)表明在運(yùn)行時(shí)可以使用反射來(lái)訪問這個(gè)注解。如果沒有設(shè)置這個(gè)指令,在運(yùn)行時(shí)這個(gè)注解將不會(huì)被保存,因此通過(guò)反射訪問不到。
@Target(ElementType.TYPE)表明注解僅能用于類、接口(包括注釋類型)或枚舉聲明等類型上。你也可以指定METHOD或FIELD,或者@Target什么都不指定,這樣它可以用在任何程序元素上。10.2 類注解
可以在運(yùn)行時(shí)獲取類、方法或字段的注解。下面是獲取類注解的示例:
Class aClass = TheClass.class;Annotation[] annotations = aClass.getAnnotations();for(Annotation annotation : annotations){if(annotation instanceof MyAnnotation){MyAnnotation myAnnotation = (MyAnnotation) annotation;System.out.println("name: " + myAnnotation.name());System.out.println("value: " + myAnnotation.value());}}也可以獲取指定的類注解,如下:
Class aClass = TheClass.class;Annotation annotation = aClass.getAnnotation(MyAnnotation.class);if(annotation instanceof MyAnnotation){MyAnnotation myAnnotation = (MyAnnotation) annotation;System.out.println("name: " + myAnnotation.name());System.out.println("value: " + myAnnotation.value());}10.3 方法注解
下面是方法注解的示例:
public class TheClass {@MyAnnotation(name="someName", value = "Hello World")public void doSomething(){}}獲取方法的所有注解,如下:
Method method = ... //obtain method objectAnnotation[] annotations = method.getDeclaredAnnotations();for(Annotation annotation : annotations){if(annotation instanceof MyAnnotation){MyAnnotation myAnnotation = (MyAnnotation) annotation;System.out.println("name: " + myAnnotation.name());System.out.println("value: " + myAnnotation.value());}}獲取方法的特定注解:
Method method = ... // obtain method objectAnnotation annotation = method.getAnnotation(MyAnnotation.class);if(annotation instanceof MyAnnotation){MyAnnotation myAnnotation = (MyAnnotation) annotation;System.out.println("name: " + myAnnotation.name());System.out.println("value: " + myAnnotation.value());}10.4 參數(shù)注解
也可以給方法聲明的參數(shù)添加注解,如下:
public class TheClass {public static void doSomethingElse(@MyAnnotation(name="aName", value="aValue") String parameter){}}從Method對(duì)象獲取參數(shù)注解:
Method method = ... //obtain method objectAnnotation[][] parameterAnnotations = method.getParameterAnnotations();Class[] parameterTypes = method.getParameterTypes();int i=0;for(Annotation[] annotations : parameterAnnotations){Class parameterType = parameterTypes[i++];for(Annotation annotation : annotations){if(annotation instanceof MyAnnotation){MyAnnotation myAnnotation = (MyAnnotation) annotation;System.out.println("param: " + parameterType.getName());System.out.println("name : " + myAnnotation.name());System.out.println("value: " + myAnnotation.value());}}}注意
Method.getParameterAnnotations()方法返回的是二維的Annotation數(shù)組,每個(gè)方法參數(shù)都有一個(gè)一維的Annotation數(shù)組。10.5 字段注解
下面是字段注解示例:
public class TheClass {@MyAnnotation(name="someName", value = "Hello World")public String myField = null;}獲取字段的所有注解,如下:
Field field = ... //obtain field objectAnnotation[] annotations = field.getDeclaredAnnotations();for(Annotation annotation : annotations){if(annotation instanceof MyAnnotation){MyAnnotation myAnnotation = (MyAnnotation) annotation;System.out.println("name: " + myAnnotation.name());System.out.println("value: " + myAnnotation.value());}}獲取指定字段的注解,如下:
Field field = ... // obtain method objectAnnotation annotation = field.getAnnotation(MyAnnotation.class);if(annotation instanceof MyAnnotation){MyAnnotation myAnnotation = (MyAnnotation) annotation;System.out.println("name: " + myAnnotation.name());System.out.println("value: " + myAnnotation.value());}11. 泛型
經(jīng)常在文章和論壇里讀到說(shuō)所有Java泛型信息在編譯的時(shí)候被擦除了所以在運(yùn)行時(shí)訪問不到任何泛型信息。這并不完全正確。在極少數(shù)的情況下,在運(yùn)行時(shí)是可以訪問泛型信息的。這些情況實(shí)際上涵蓋一些我們需要的Java泛型信息。本章將解釋這些情況。下面是本章涵蓋的主題:
(1) 泛型反射的經(jīng)驗(yàn)法則
(2) 泛型方法的返回值類型
(3) 泛型方法的參數(shù)類型
(4) 泛型字段類型
11.1 泛型反射的經(jīng)驗(yàn)法則
使用Java的泛型通常分為兩種不同的情況:
(1)聲明一個(gè)可參數(shù)化的類/接口。
(2)使用參數(shù)化的類。
當(dāng)寫一個(gè)類或接口時(shí),可以指定它應(yīng)該是可參數(shù)化的。就像
java.util.List接口那樣,可以參數(shù)化java.util.List來(lái)創(chuàng)建一個(gè)String列表而不是創(chuàng)建Object列表。java.util.List在運(yùn)行時(shí)會(huì)檢查參數(shù)化的類型,但是沒有辦法知道參數(shù)化的是什么類型。這是因?yàn)樵谙嗤膽?yīng)用程序中類型可以被參數(shù)化為所有的類型。但是,當(dāng)你檢查方法或字段所聲明使用的參數(shù)化類型,你可以在運(yùn)行時(shí)看到可參數(shù)化的類型被參數(shù)化為什么類型。總之,你不能在運(yùn)行時(shí)看到類型本身被參數(shù)化為什么類型,但你可以在使用和參數(shù)化它的字段和方法中看到它。
11.2 泛型方法的返回值類型
如果你獲取到一個(gè)
java.lang.reflect.Method對(duì)象,可以獲取它的返回值類型信息。下面是一個(gè)示例,一個(gè)類有一個(gè)有參數(shù)化返回值類型的方法:public class MyClass {protected List<String> stringList = ...;public List<String> getStringList(){return this.stringList;}}在這個(gè)類中可以獲取
getStringList()方法的泛型返回值類型。換句話說(shuō),可以探測(cè)到getStringList()返回的是List<String>而不僅是一個(gè)List。如下:Method method = MyClass.class.getMethod("getStringList", null);Type returnType = method.getGenericReturnType();if(returnType instanceof ParameterizedType){ParameterizedType type = (ParameterizedType) returnType;Type[] typeArguments = type.getActualTypeArguments();for(Type typeArgument : typeArguments){Class typeArgClass = (Class) typeArgument;System.out.println("typeArgClass = " + typeArgClass);}}代碼輸出 "typeArgClass = class java.lang.String"。
Type[]數(shù)組typeArguments包含一項(xiàng) -一個(gè)代表類java.lang.String的Class實(shí)例。Class實(shí)現(xiàn)了Type接口。11.3 泛型方法的參數(shù)類型
通過(guò)Java反射可以在運(yùn)行時(shí)訪問參數(shù)類型的泛型類型。下面的示例中,類有一個(gè)使用參數(shù)化的List作為參數(shù)的方法:
public class MyClass {protected List<String> stringList = ...;public void setStringList(List<String> list){this.stringList = list;}}可以訪問方法參數(shù)的泛型參數(shù)類型,如下:
Method method = Myclass.class.getMethod("setStringList", List.class);Type[] genericParameterTypes = method.getGenericParameterTypes();for(Type genericParameterType : genericParameterTypes){if(genericParameterType instanceof ParameterizedType){ParameterizedType aType = (ParameterizedType) genericParameterType;Type[] parameterArgTypes = aType.getActualTypeArguments();for(Type parameterArgType : parameterArgTypes){Class parameterArgClass = (Class) parameterArgType;System.out.println("parameterArgClass = " + parameterArgClass);}}}代碼輸出"parameterArgType= class java.lang.String"。