Java反射機制Reflection


Java反射機制

了解Java的反射機制,首先要從RTTI開始。

文章的一開頭就開始裝逼了,RTTI是個什么東西。RTTI英文全稱:Run-Time Type Information,望文生義,RTTI是運行時類型信息。

什么是RTTI,有什么作用,編程時會用到嗎?這個問題先放在這里,等會再解答。

 

1,  .class文件

學過一點點Java的人,都知道Java文件在編譯之后變成了class文件。問題又來了,class文件是什么?肯定有人回答:Class文件就是一個編譯后的文件,就像C/C++.c.cpp文件編譯之后編程了.o,還需要解釋嗎?要,必須要。

先來一張class文件的文件格式示意圖。

  

 

沒看懂?必然看不懂,先簡單介紹一下Class文件。

Class文件從文件類型上是二進制文件,問題又來了,什么是二進制文件?二進制文件就是機器能讀懂的語言,自從馮諾依曼的原型機出世,所有的計算機作為它的后代就只認識10這兩個數字。二進制文件就是一堆01Class文件就是由一堆01堆起來的。但是,無規矩不成方圓,這些01是按照某種嚴格的規則放置在對應的位置上。JVM作為Java的運行環境,是規則的制定者和解讀者(盡管是Java的開發者們制定的,我們可以簡單地認為JVM做了這些事情,至少是開發者們讓JVM這么干的)。JVM按照規則來解讀01序列,然后告訴計算機如何去執行我們的程序所表達的意願。

但是,Class文件的結構不像XML那些描述型語言那樣松散和自由,它的定義是非常嚴格的,條件也非常的苛刻。Class文件沒有任何的分割符號,哪怕是一個空格一個回車(關於回車為什么叫做回車,點擊這里)。

在圖中Class文件的幾個部分,HeaderConstant pool...等,這些數據項無論是順序還是數量都是被嚴格限定的,哪個字節代表什么含義,長度是多少,先后順序如何都被非常苛刻地限制,不允許改變。然后,你會看到Class attributesclass文件的最后,應該用過Java反編譯器吧,有沒有注意過Java反編譯器反編譯的class文件方法在上面,屬性都在下面,就是這個原因。

 

這篇博客講解的很詳細,http://blog.csdn.net/dc_726/article/details/7944154

文件的開頭是Header,這段十六進制碼表明了JDK的版本號,所以你把JDK1.7class文件放在JDK1.6的環境下就不能運行,向下兼容。

 

2, Class

扯得有點遠了。回到正題。

Java被編譯后,生成了.class文件,JVM此時就要去解讀.class文件。當程序主動去使用某個類時,如果這個類還沒有被加載到內存中,JVM會通過三個步驟對類進行初始化:

1.加載:由類加載器執行,該步驟查找字節碼,並從這些字節碼中創建一個Class對象

2.鏈接:在鏈接階段將驗證類中的字節碼,為靜態域分配存儲空間,並且如果必須的話,將解析這個類創建的對其他類的所有引用。

3.初始化:如果該類有超類,則對其初始化,執行靜態初始化器和靜態初始化塊。

如以上三步,一個.class文件被融入到程序中供其調用。第一個步驟中,查找字節碼並生成一個Class對象,在Java中,如Thinking in Java中提到的,一切皆是對象。被編譯后的Java文件.class也被JVM解析為一個對象,這個對象就是java.lang.Class

 

這樣當程序在運行時,每個java文件就最終變成了Class類對象的一個實例。我們通過Java的反射機制應用到這個實例,就可以去獲得甚至去添加改變這個類的屬性和動作,使得這個類成為一個動態的類(所謂的動態是按照動態語言的定義——在程序運行時改變其結構:心得函數可以被引進,已有的函數可以被刪除等在結構上的變化)。 由此,可以看出反射機制使得Java語言多么靈活。

 

3,Class類與反射機制

Class類的概念盡管很抽象,但是無疑,它是反射機制的起源,是Java語言中一個精巧美妙地設計。

介紹完.class文件是怎么回事,又簡單說明了.classClass之間的關系,然后要看一下Class類的官方描述。

官方文檔是最權威的,先來看一下Class類中的API是如何描述Class類的。下面是翻譯后的中文文檔的描述:

Class類的實例表示正在運行的Java應用程序的類和接口。枚舉是一種類,注釋(注解)是一種接口。每個數組屬於被映射為Class對象的一個類,所有具有相同元素類型和維數的數組都共享該Class對象。基本的Java類型(booleanbytecharshortintlongfloat 和 double)和關鍵字 void 也表示為 Class 對象。Class沒有公用構造方法。Class對象是在加載類時由JVM以及通過調用類加載器中的defineClass方法自動構造的。

看完這個描述,並結合上面提到的三個步驟,應該會有更好的理解。

 4,反射機制的定義和應用

在深入到反射機制之前,先探析一下反射機制的定義和應用。反射機制定義:Java反射機制是在運行狀態時,對於任意一個類,都能夠直到這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意一個方法和屬性。

我們不禁會想,得到這些屬性和方法並且去改變它們有什么用?覺得反射機制仿佛很遙遠,但是作為一個全職程序員,除了假期和雙休日,你幾乎每天都在和反射機制打交道,無論你使用的Eclipse系列的IDE(集成開發環境),還是使用的目前較為流行的IntelliJ IDEA,又或者是NetBeans,在你輸入一個對象的名稱,按下”.”時,總會彈出這個對象所擁有的所有方法列表供選擇。這個功能就是反射機制實現的,反射機制得到了這個對象的方法和屬性。

很顯然,是先有的反射機制,才有的自動智能提示,所以上面的例子只能勉強算作反射機制出現和應用的一個動機。

在反射機制設計之初,睿智的設計者們或許就有了分布式的野心。編程人員希望Java能夠提供在跨網絡的遠程平台上創建和運行對象的能力,這種能力的名字大家應該很熟悉,叫做遠程方法調用RMI,這便是編程人員想要在運行時獲取類的信息的又一個動機。RMI允許一個Java程序將對象分布到多台機器上,即在遠程就可以調用某個對象,就像你通過瀏覽器去使用別人提供的網頁服務一樣。這就促成了現在的分布式系統的崛起。然而在JDK1.1版本中,Java反射機制就出現了。

 

5,Java反射機制的類庫支持及簡介

Class類和java.lang.reflect類庫一起構成了對Java反射機制的支持。其中最常使用到的類是ConstructorFieldMethod,而這三個類都繼承了一個接口java.lang.reflect.Member。下面列舉介紹一下java.lang.reflect類庫中的類:

  • AccessibleObjectFieldMethod,和Constructor對象的基類。提供了將反射的對象標記為在使用時取消默認Java語言訪問控制檢查的能力。
  • Array:提供了動態創建和訪問Java數組的方法。
  • Constructor:提供關於類的單個構造方法的信息以及對它的訪問權限。
  • Field: 提供有關類或接口的單個字段的信息,以及對它的動態訪問權限。反射的字段可能是一個類(靜態)字段或實例字段。
  • Method: 提供關於類或接口上單獨某個方法(以及如何訪問該方法)的信息。所反映的方法可能是類方法或實例方法(包括抽象方法)。
  • Modifier: 類提供了 static 方法和常量,對類和成員訪問修飾符進行解碼。修飾符集被表示為整數,用不同的位位置 (bit position) 表示不同的修飾符。該類的字段均是int類型的變量。 
  • Proxy: 提供用於創建動態代理類和實例的靜態方法,它還是由這些方法創建的所有動態代理類的超類。
  • ReflectPermission:反射操作的 Permission 類。ReflectPermission 是一種指定權限,沒有動作。當前定義的唯一名稱是 suppressAccessChecks,它允許取消由反射對象在其使用點上執行的標准 Java 語言訪問檢查 對於 publicdefault(包)訪問、protectedprivate 成員。

 

當要使用反射機制去探查一個類的內部時,還可以調用getFields()getMethods()getConstructors()等很便利的方法。對於反射機制,和RTTI的區別就在於,RTTI是在編譯時打開和檢查.class文件,而反射機制是在運行時打開和檢查.class文件。

 

6,反射機制使用Demo

1.Class類是反射機制的起源,我們得到Class類對象有三種方法:

Class.forName()

Object.getClass()

類字面常量xx.class

  

解釋一下這三種方法,

  • 第一種方法是Class類自帶的方法,
  • 第二種方法是所有的對象都能夠使用的方法,因為getClass()方法是Object類的方法,所有的類都繼承了Object,因此所有類的對象也都具有getClass()方法。
  • 第三種方法是類字面常量。Thinking in Java中建議使用類字面常量來生成對Class對象的引用,這樣做即簡單又安全,因為在編譯時就會受到檢查,因此不需要置於try語句塊中,並且它根除了對forName()方法的調用,所以也更高效。可以想象一下JDBC的語法,在加載驅動的時候,使用的就是forName()方法,因此即使單獨這一句程序,也要使用try語句塊。

類字面常量使得創建Class對象的引用時不會自動地初始化該對象,而是按照之前提到的加載,鏈接,初始化三個步驟,這三個步驟是個懶加載的過程,不使用的時候就不加載,這種機制是C/C++無法復制模擬的。

 

其他DEMO

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM