国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 編程 > Java > 正文

java反射機(jī)制

2019-11-11 07:46:46
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

前言

    本文是我整理的java反射的一些知識(shí),其中大部分內(nèi)容是翻譯http://tutorials.jenkov.com/java-reflection/index.html的。

1. Java反射簡(jiǎn)介

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)代理。

2. Class對(duì)象

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類型(booleanbytecharshortintlongfloatdouble)和關(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

 

3. 類

使用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) 注解

3.1類名

    從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ù)的constructor
Constructor 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.class
Field 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.class
Field 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ì)的介紹 Java Method對(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 above
Class returnType = method.getReturnType();

6.3使用Method對(duì)象調(diào)用方法

可以像這樣調(diào)用方法:

//get method that takes a String as argument
Method 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] = 123
intArray[1] = 456
intArray[2] = 789

9.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.String Class對(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 object
Annotation[] 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 object
Annotation 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 object
Annotation[][] 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 object
Annotation[] 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 object
Annotation 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"。

 Type[]數(shù)組 parameterArgTypes包含一項(xiàng) -一個(gè)代表類java.lang.String Class實(shí)例 。 Class實(shí)現(xiàn)了 Type接口。

11.4 泛型字段類型

可以訪問public字段的泛型類型。字段即類的成員變量-靜態(tài)的或?qū)嵗兞俊O旅媸且粋€(gè)例子,類有一個(gè)實(shí)例變量stringList.

public class MyClass {
         public List<String> stringList = ...;
}
Field field = MyClass.class.getField("stringList");
Type genericFieldType = field.getGenericType();  
if(genericFieldType instanceof ParameterizedType){
         ParameterizedType aType = (ParameterizedType) genericFieldType;
         Type[] fieldArgTypes = aType.getActualTypeArguments();
         for(Type fieldArgType : fieldArgTypes){
            Class fieldArgClass = (Class) fieldArgType;
            System.out.println("fieldArgClass = " + fieldArgClass);
          }
}

代碼將輸出"fieldArgClass = class java.lang.String"。

 Type數(shù)組fieldArgTypes包含一項(xiàng) – 一個(gè)代表類java.lang.String Class實(shí)例 。 Class實(shí)現(xiàn)了 Type接口。


發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 罗田县| 兴国县| 班玛县| 仙居县| 噶尔县| 铅山县| 南江县| 开阳县| 河曲县| 南陵县| 曲周县| 哈密市| 宝鸡市| 绥江县| 临洮县| 鄱阳县| 醴陵市| 农安县| 隆回县| 彩票| 巩义市| 融水| 云南省| 洛川县| 望奎县| 密山市| 五大连池市| 双鸭山市| 邹平县| 浪卡子县| 盐山县| 温泉县| 桃园市| 封开县| 宿州市| 无棣县| 濮阳市| 蕉岭县| 翁源县| 鄢陵县| 宝山区|