java反射

每個類被加載之後,系統就會爲該類生成一個對應的Class對象,通過該Class對象就可以訪問到JVM中的這個類。Java程序中獲得Class對象通常有如下三種方式:
1).使用Class類的forName()靜態方法。該方法需要傳入字符串參數,該字符串參數的值是某個類的全限定類名(必須添加完整的包名)
2).調用某個類的class屬性來獲取該類對應的Class對象。例如Person.class將會返回Person類對應的
Class對象。
3).調用某個類的getClass()方法,該方法是java.lang.Object類中的一個方法,所以所有Java對象都可以調用該方法,該方法將會返回該對象所屬類對應的Class對象
   
   對於第一種方式和第二種方式都是直接根據類來取得該類的Class對象,但相比之下,第二種方式有如下兩種優勢:
(1).代碼更安全,程序在編譯階段就可以檢查需要訪問的Class對象是否存在。
(2).程序性能更高,因爲這種方式無須調用方法,所以性能更好。
   也就是說,大部分時候我們應該使用第二種方式來取得指定類的Class對象,但是如果我們只有一個字符串,如"java.lang.String",如果需要獲取該字符串對應的Class對象,則只能使用第一種方式了,使用Class的forName()方法來獲取Class對象時,該方法可能拋出一個ClassNotFoundException異常。
    一旦獲得了某個類所對應的Class對象之後,程序就可以調用Class對象的方法來獲得該對象和該類的真實信息了。


從Class中獲取信息

四個方法用於訪問Class對應的類所包含的構造器:
1).Connstructor<T>  getConstructor(Class<?>...parameterTypes):返回此Class對象所表示的類的指定的public 構造器
2).Constructor<?>[]  getConstructors():返回此Class對象所表示的類的所有public構造器。
3).Constructor<T>  getDeclaredConstructor(Class<?>...ParameterTypes):返回此Class對象所表示的類的指定構造器,與構造器的訪問級別無關。
4).Constructor<?>[] getDeclaredConstructors():返回此Class對象所表示的所有構造器,與構造器的訪問級別無關

四個方法用於訪問Class對應的類所包含的方法:
1).Method  getMethod(String name,Class<?>...parmeterTypes):返回此Class對象所表示的類的指定的指定的public 方法
2).Method[]  getMethod():返回此Class對象所表示的類的所有public 方法。
3).Method  getDeclaredMethods(String name,Class<?>...parameterTypes):返回此Class對象所表示的類的指定方法,與方法的訪問級別無關。
4).Method[]  getDeclaredMethods():返回此Class對象所表示的類的全部方法,與方法的訪問級別無關。

如下四個方法用於訪問Class對應的類所包含的屬性(Field):
1).Field  getField(String name):返回此Class對象所表示的類的指定的public屬性(Field)。
2).Field[]  getFields():返回此Class對象所表示的類的所有public屬性(Field)。
3).Field  getDeclaredFields(String name):返回此Class所表示的類的指定屬性(Field),與屬性的訪問級別無關。
4).Field[]  getDeclaredFields(String name):返回此Class所表示的類的全部屬性(Field),與屬性的訪問級別無關。


如下三個方法用於訪問Class對應的類上所包含的註釋:
1).<A  extends  Annotation>  A getAnnotation(Class<A> annotationClass):試圖獲取該Class對象所表示類上指定類型的註釋;如果該類型的註釋不存在則返回null.
2).Annotation[]  getAnnotations():返回此元素上存在的所有註釋。
3).Annotation[]  getDeclaredAnnotations():返回直接存在於此元素上的所有註釋。

 

如下方法用於訪問該Class對象對應類所包含的內部類:
1).Class<?>[]  getDeclaredClasses():返回此Class對象所對應類裏包含的全部內部類。


如下方法用於訪問該Class對象對應類所在的外部類:
1).Class<?>[]  getDeclaringClass():返回此Class對象所對應類所在的外部類。


如下方法用於訪問該Class對象所對應類所繼承的父類,所實現的接口等:
1).Class<?>[]  getInterfaces():返回該Class對象對應類所實現的全部接口。
2).int  getModifiers():返回此類或接口的所有修飾符。修飾符由public,protected,private,final,static,abstract等對應的常量組成,返回的整數應使用Modifier工具類的方法來解碼,纔可以獲取真實的修飾符。
3).Package  getPackage():獲取此類的包。
4).String  getName():以字符串形式返回此Class對象所表示的類的名稱。
5).String  getSimpleName():以字符串形式返回此Class對象所表示的類的簡稱。
6).Class<? super T>  getSuperclass():返回該Class所表示的類的超類對應的Class對象。

除此之外,Class對象還可以調用如下幾個判斷來判斷該類是否爲接口,枚舉,註釋類型等:
1).boolean  isAnnotation():返回此Class對象是否表示一個註釋類型(由 @interface定義)。
2).boolean  isAnnotationPresent(Class<?  extends  Annotation>  annotationClass):返回此Class對象上是否使用了Annotation註釋的修飾符。
3).boolean  isAnonymousClass():返回此Class對象是否是一個匿名類。
4).boolean  isArray():返回此Class對象是否表示一個數組類。
5).boolean  isEnum():返回此Class對象是否表示一個枚舉(由enum關鍵字定義)。
6).boolean  isInterface():返回此Class對象是否表示一個接口(使用interface定義)。
7).boolean  isInstance(Object  obj):判斷obj是否是此Class對象的實例,該方法可以完全代替instanceof操作符。


上面的多個getMethod方法和getConstructor多個方法中,都有一個需要傳入參數類型爲Class<?>,個數可變的參數,用於獲取指定的方法或指定的構造器。關於這個參數的作用,假設某個類內部包含如下三個info方法:
public  void  info()

public  void  info(String str)

public  void  info(String str,Integer  num)

這兩個同名方法屬性重載,它們的方法名相同,但是參數列表不同。在Java語言中要確定一個方法光有方法名是不行的,例如我們指定info方法---實際上可以是上面三個方法中的任意一個!如果需要確定一個方法,應該由方法名和形參列表來確定,但形參名沒有任何實際意義,所以只能由形參類型來確定。例如我們想要確定第二個info方法,必須指定方法名是info,形參類型是String.class----因此程序中獲取該方法使用如下代碼:
//前一個參數指定方法名,後面的個數可變的Class參數指定參數類型列表
clazz.getMethod("info",String.class)
獲取第三個info方法,則使用如下代碼:
clazz.getMethod("info",String.class,Integer.class)


獲取構造器時無須傳入構造器名----同一個類的所有構造器的名字都是相同的,所以要確定一個構造器只要指定形參列表即可。

 


使用反射生成並操作對象:
    Class對象可以獲得該類裏的成分包括方法(由Method對象表示),構造器(由Constructor對象表示),Field(由Field對象表示),這三個類都定義在java.lang.reflect包下,並實現了java.lang.reflect.Member接口,程序可以通過Method對象來執行對應的方法,通過Constructor對象來調用對應的構造器創建對象,能通過Field對象直接訪問並修改對象的屬性值。

創建對象:
    通過反射來生成對象有如下兩種方式:
1).使用Class對象的newInstance()方法來創建該Class對象對應類的實例,這種方式要求該Class對象的對應類有默認構造器,而執行newInstance()方法實際上是利用默認構造器來創建該類的實例。
2).先使用Class對象獲取指定的Constructor對象,再調用Constructor對象的newInstance()方法來創建該Class對象對應類的實例。通過這種方式可以選擇使用某個類的指定構造器來創建實例。


    通過第一種方式來創建對象是比較常見的情形,因爲在很多Java EE框架中都需要根據配置文件信息來創建Java對象,從配置文件兌取的只是某個類的字符串類名,程序就需要根據字符串來創建對應的實例,就必須使用反射。

   如果我們不想利用默認構造器來構建Java對象,而想利用指定的構造器來創建Java對象,則需要利用Constructor對象了,每個Constructor對應一個構造器。爲了利用指定構造器來創建Java對象需要如下三步:
1).獲取該類的Class對象
2).利用Class對象的getConstructor()方法來獲取指定構造器。
3).調用Constructor的newInstance()方法來創建Java對象。


調用方法:
   
    當獲得某個類對應的Class對象後,就可以通過該Class對象的getMethods()方法或者getMethod()方法來獲取全部方法或指定方法——這兩個方法的返回值是Method對象組,或者Method對象。

    每個Method對象對應一個方法,獲得Method對象後,程序就可以通過該Method來調用對應方法,在Method裏包含一個invoke()方法,該方法的簽名如下:
1).Object  invoke(Object obj,Object...args):該方法中的obj是執行該方法的主調,後面的args是執行該方法的實參。


訪問屬性值:

    通過Class對象的getFields()或getField()方法可以獲取該類所包括的全部Field(屬性)或指定Field.Field提供瞭如下兩組方法來訪問屬性:

1).getXxx(Object obj):獲取obj對象該Field的屬性值。此處的Xxx對應8個基本類型,如果該屬性的類型是引用類型則取消get後面的Xxx。
2).setXxx(Object  obj,Xxx  val):將obj對象的該Field設置爲val值。此處的Xxx對應8個基本類型,如果該屬性的類型是引用類型則取消set後面的Xxx。

 

發佈了27 篇原創文章 · 獲贊 4 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章