杜叔強
摘 要:反射是Java語言中的一種重要的編程機制;總結了Java語言中反射的原理,以及反射相關類的獲取方式,并總結了反射機制的幾點應用。
關鍵詞:反射機制 Class Constructor Field Method
1反射的概念
Java反射是Java被視為動態(或準動態)語言的一個關鍵性質。這個機制允許程序在運行時通過反射API取得任何一個已知名稱的類的內部信息,包括其訪問修飾符、父類、實現的接口,也包括屬性和方法的所有信息,并可在運行時改變屬性值或進行方法調用。Java反射機制容許程序在運行時加載、探知、使用編譯期間完全未知的類。也就是說,Java可以加載一個運行時才得知名稱的類,并且獲得其完整結構信息,這種動態獲取信息以及動態調用對象的成員的功能稱為java語言的反射機制[1]。
2反射的原理
Java反射機制的實現要借助于4個類:Class,Constructor,Field,Method;其中Class代表的是類對象,Constructor是類的構造器對象,Field是類的屬性對象,Method是類的方法對象,通過這四個對象我們可以粗略的看到一個類的各個組成部分。其中最核心的就是Class類,它是實現反射的基礎。Class類的實例就是某個類的描述信息。Class類的實例表示正在運行的Java應用程序中的類和接口。
Class沒有公共構造方法。Class對象是在加載類時由Java虛擬機以及通過調用類加載器中的defineClass方法自動構造的。JVM在程序第一次主動使用某個類的時候,才會去加載該類。也就是說,JVM并不是在一開始就把一個程序所有的類都加載到內存中,而是到用的時候才把它加載進來,而且只加載一次。當JVM加載某個類時,會到本地磁盤去找到這個類的字節碼文件,然后將這個字節碼文件加載到JVM內存中,并且在內存堆區創建這個類的Class對象。注意這個不是new出來的對象,而是類的類型描述對象,每個類只有一個Class對象,作為類的數據結構的接口。JVM創建對象前,會先檢查類是否加載,尋找類對應的Class對象,若類已加載好,則為待創建對象分配內存。有了類型信息描述對象Class對象,就可以獲取類的屬性,方法等信息。
3 Class對象的獲取
有三種方式可以獲取Class對象[2]。
(1)通過類.class屬性獲得。任何數據類型都有一個“靜態”的class屬性。Java在編譯一個類文件時,會為該類動態地添加一個公有的靜態常量屬性class,這個屬性記錄了該類的相關信息,即類型描述信息,它是Class類的實例。
(2)通過對象.getClass()方法獲得。任何對象都可以調用getClass()返回表示此對象運行時類的Class對象。當得到一個對象引用而不知道所屬的類時,就可以用此方法得到該對象所屬類的Class對象。
(3)通過Class.formName()靜態方法獲得。Java的Class提供了靜態方法formName(),此方法顯示地加載指定類,并返回被加載類的Class對象。
4 Constructor對象的獲取
Constructor類的對象用于描述類的單個構造方法。Class對象提供了四個方法可以獲取Constructor對象。
(1)getConstructor(Class parameterTypes…)獲取指定參數類型的公有Constructor對象。
(2)getConstructors()獲取指定類的公有構造方法描述對象Constructor列表。
(3)getDeclaredConstructor(Class parameterTypes…)獲取指定參數類型的構造方法描述對象。
(4)getDeclaredConstructors()獲取指定類的所有構造方法描述對象列表。
5 Method對象的獲取
Method類的對象用于描述類的單個方法(不包括構造方法)。可以通過Method類來獲取方法的訪問權限、參數類型、返回值類型等信息,并且可以通過獲取的Method對象來動態執行方法。Class對象提供了四個方法可以獲取Method對象。
(1)getMethod(String name,Class parameterTypes…)獲取指定名稱和參數類型的公有方法描述對象。
(2)getMethods()獲取公有的方法描述對象列表。
(3)getDeclaredMethod(String name, Class parameterTypes…)獲取指定名稱和參數類型的方法描述對象。
(4)getDeclaredMethods()獲取類本身定義的所有方法描述對象。
6 Field對象的獲取
Field類的對象用于描述類的單個屬性。可以通過Field對象來獲取屬性的訪問權限、屬性類型等信息,并且可以通過獲取的Field對象來動態地修改屬性值。Class對象也提供了四個方法可以獲取Field對象。
(1)getField(String name)獲取指定名稱的公有Field對象。
(2)getFields()獲取指定類的公有屬性描述對象Field列表。
(3)getDeclaredField(String name)獲取指定名稱的Field對象。
(4)getDeclaredFields()獲取指定類的所有屬性描述對象Field列表。
7反射的應用
(1)運行時類型識別
Class對象的isInstance()方法其原型是public boolean isInstance(Object obj),這個方法用來判定指定的對象是不是類的實例。例如從一個容器中取得了對象后,就可以判定這個對象的所屬的類。
(2)獲取資源文件的URL
Class對象的getResource()方法其原型是public URL getResource(String name),此方法可以返回與給定類相關的指定名稱的資源URL。如果程序中用到圖片、音頻等資源,可以將這些資源放到相關類字節碼文件相同目錄,便于程序打包成jar文件。
(3)動態獲取類型信息
在程序中可以通過反射顯式加載指定類,通過反射實例化類,還可以通過反射執行方法,修改屬性值和訪問權限等操作。
8小結
Java中反射機制很實用,靈活使用反射能讓我們代碼更加靈活。但是反射也有缺點,反射包括了一些動態類型,所以 JVM 無法對這些代碼進行優化。因此,反射操作的效率要比那些非反射操作低得多。我們應該避免在經常被執行的代碼或對性能要求很高的程序中使用反射[3]。
參考文獻:
[1]Java基礎之—反射[EB/OL].[2018-03-17].http://blog.csdn.net/sinat_38259539/article/details/71799078
[2]徐傳運 張楊.Java高級程序設計[M].北京:清華大學出版社,2014.
[3]粗淺看 java反射機制[EB/OL].[2018-03-17.http://blog.csdn.net/wsl211511/article/details/51605655