Java為什麼能夠支援Reflection?答案是Java運行時仍然擁有類型資訊,它包含了這個類一切:它有哪些欄位、哪些方法,各是何種保護層級等等,還有這個類依賴於哪些類。在Java中,類資訊以對象的形式存放,這些對象是一種元對象,它們的類型就是Class。擁有了這些資訊,無論是動態建立對象還是調用某些方法都是輕而易舉。在C++中,通過RTTI(運行時類型識別),我們也可以知道類的一些資訊,但為什麼C++中卻沒有 Reflection,原因是類型資訊不完整。RTTI這個名字本身就告訴我們,C++的類型資訊是用來進行類型識別的,因此,它也不需要其它額外的資訊。並不是C++無法做到這一點,而是C++不希望給使用者增加額外的負擔。有所得,必然有所失,因此,C++放棄了元對象。關於這一點,C++之父 Bjarne Stroustrup在他的《C++語言的設計與演化》的14.2.8節中進行了深入的討論。
元對象是Java Reflection的物質基礎,那它的精神基礎又是什麼呢?Java為什麼要支援Reflection?經過上面的討論,我們把這個問題再進一步,為什麼Java要提供元對象?
討論這個問題,我們還要拉回到十年前,那時Java剛剛來到正式登上曆史的舞台。Java實際上誕生在這之前的數年,那時候還叫Oak,環境所限使得這一划時代的傑作甫一出爐便被束之高閣。當Netscape掀起了為網路大戲的序幕,Java得以鳳凰涅槃,這其中很重要的一個原因就是Java是以網路為中心的。
仔細觀察,我們會發現,Java的整個基礎架構的設計都是為網路服務。首當其衝的便是Java中最著名的跨平台。其實,在Java之前的年代,人們也需要考慮平台之間的可移植性,但這種移植大多數集中在源碼一級,這也就是C語言可以流行的原因之一,在單機環境下,平台的差異並不那麼明顯。網路的出現使平台之間差異凸現出來,因為網路可能會串連各種各樣的電腦和裝置。沒錯,還有裝置,你也許知道Java最初的開發是和嵌入式裝置相關的。一旦應用可以跨平台,程式開發和後期管理維護工作將得到極大的簡化,可移植性也從源碼級晉陞到二進位級(Java位元組碼)。所以,跨平台實際上也是為了網路打基礎。 Java中另一個重要的買點——安全性與網路之間的關係更為密切,誰都可以想出幾條理由,把二者關聯起來。
再來具體看看Java的基礎架構如何對網路進行支援的。還記得Java最初是怎麼迷人的嗎?沒錯,Applet。熟悉原理的朋友都知道,Applet的運行是把遠端類檔案下載到本地來執行的。相對於本地硬碟,網路給我們的感覺就是一個字————慢。如果Java採用傳統可執行檔組織方式,即一個完整的可執行檔,把整個Applet下載下來的運行,只怕等到花兒也謝了。Java採用的手法是把檔案拆開,以類為單位進行組織,這就是我們今天見到的class檔案。這樣,執行的過程就變成第一個類下載之後就可以運行,大大節省了最初的等待時間。好的設計會把程式分成若干的模組,所以,絕大多數程式不可能寫在一個類中。因此,類檔案中必須包含它所用到類。對於引導部分,我們可以讓它以特定的方式開始執行,比如把我們耳熟能詳的main方法放在特定的位元組,但對於沒有定法的任意方法,是沒有辦法規定的,而一個類調用另一個類的方法就是這樣隨意,因此類檔案中必須包含這個類方法的資訊,進一步欄位資訊也會加進來,這樣幾乎一個完整類的資訊就出來了,而這些資訊對應的恰好是元對象。所以,元對象出現在Java基礎架構中。
有了元對象,Reflection也成了一件順其自然的事情。有了Reflection,Java也就擁有了動態擴充的能力,這樣就可以極大的提高程式的靈活性。
關於Java基礎結構對網路的支援還可以再說幾句。class檔案經過了精心的設計,本身相當緊湊,其目的就是為了方便在網路上傳輸,而JAR檔案的出現,其目的也是為了方便網路傳輸,因為如果每次只傳輸一個類,大量的時間都被浪費在建立網路連接的過程中,JAR檔案使得一次傳輸多個類成為可能,而且我們還知道JAR檔案中的資料是經過壓縮的,這樣可以進一步減少下載時間。Java基礎架構對網路的支援,《深入Java虛擬機器》(第二版)的4.3節進行了很好闡述,有興趣不妨看一下。
對Reflection思考讓我有機會對Java本身的設計進行深入的思考。一個好的軟體設計需要一個核心理念作為支撐,所有的一切都是圍繞核心進行的,而對於Java,這個核心就是網路。