字節碼(Byte Code)是Java語言跨平臺特性的重要保障,也是反射機制的重要基礎。通過反射機制,我們不僅能看到一個類的屬性和方法,還能在一個類裏調用另外一個類的方法,但前提是我們得有相關類的字節碼文件(也就是.class文件)。
1 字節碼和.class文件
當我們編寫好以.java爲擴展名的文件後,如果它能被運行(比如其中包含main函數),那麼我們能通過點擊MyEclipse裏的運行按鈕運行這個.java文件。
但此時,MyEclipse向我們隱藏了一個關鍵步驟:它首先是把.java文件編譯成擴展名是.class的字節碼文件,隨後,Java的虛擬機(JVM)是在當前的操作系統上(比如window 10)上運行這個.class文件。
也就是說,.java文件是得被編譯成.class文件後才能運行,更爲神奇的是,大家在window 7系統裏編譯好的.class文件能直接在Linux系統裏運行(當然這個系統裏得有Java運行環境),這就是java的跨平臺裏特性,也叫能“一處編譯到處運行”。
有些偏題了,回到反射這個話題上,只要我們能得到.class這個字節碼文件,那麼通過反射機制我們不僅能看到這個.class所對應java文件裏的屬性方法等信息,而且還能調用它所對應java文件裏的方法。
2 Class類(C是大寫)是反射實現的語法基礎
通過某些工具,我們能打開.class文件,並也能看到其中包含的屬性和方法,但我們不能直接針對.class文件編程,我們得使用Class(C是大寫)這個類。
Class類的全稱是java.lang.Class,當一個類或接口(總之是java文件被編譯後的class文件)被裝入到Java虛擬機(JVM)時便會產生一個與和它相關聯的java.lang.Class對象,在反射部分的代碼裏,我們一般是通過這個Class來訪問和使用目標類的屬性和方法。
通過反射機制,能從.class文件裏看到指定類的屬性,比如屬性的修飾符,屬性和類型和屬性的變量名。通過下面的ReflectionReadVar.java,我們看演示下具體的做法。
我們在第3行定義了一個MyValCalss的類,並在第4到第6行裏,定義了三個屬性變量。
在main函數的第10行裏,通過MyValClass.class,得到了Class<MyValClass>類型的變量clazz,在這個變量中,存儲了MyValClass這個類的一些信息。
在第12行裏,通過了clazz.getDeclaredFields()方法得到了MyValClass類裏的所有屬性的信息,並把這些屬性的信息存入到Field數組類型的fields變量裏。
通過了第13行的for循環依次輸出了這些屬性信息。具體來講,通過第14行的代碼輸出了該屬性的修飾符,通過第16行的代碼輸出了該屬性的類型,通過第18行的代碼輸出了該屬性的變量名。這段代碼的輸出如下,從中我們能看到各屬性的信息。
- private int val1
- public class java.lang.String val2
- protected final class java.lang.String val3