反射反射,程式員的快樂!
Java中反射機制使用的還是比較廣泛的,系統的靈活性、可擴充性大都都是通過反射等方式來載入外部外掛程式,使得系統與外掛程式解耦的同時,增加了功能。但是很多人都只是會用,卻是不知道它的實現機制,今天就由我來帶大家揭開反射機制的神秘面紗。
Java中是用Class.forName(classname)來反射類。
package com.java.reflecttest;import com.java.dbtest.DBTest;/** * Java反射機制測試 * @author Longxuan * */public class ReflectTest {/** * 測試反射類 */public static void refTest(){String className = "com.java.dbtest.TestConnection";DBTest dbTest = null;try {//通過反射機制,使用類裝載器,裝載該類Class tc = Class.forName(className);//輸出反射得到的類System.out.println(tc);//建立該類的執行個體,轉化為介面dbTest =(DBTest)tc.newInstance();//通過介面,調用該類的方法dbTest.SelectUser();} catch (ClassNotFoundException e) {e.printStackTrace();} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}}public static void main(String[] args){refTest();}}
通過main函數的調試,已經通過,結果
經過調試,查資料,結合自己的推測和理解,似乎是明白了一些。現與大家分享討論。
先說執行過程:
Class.forName(classname)方法,實際上是調用了Class類中的 Class.forName(classname, true, currentLoader)方法。參數:name - 所需類的完全限定名;initialize - 是否必須初始化類;loader - 用於載入類的類載入器。currentLoader則是通過調用ClassLoader.getCallerClassLoader()擷取當前類載入器的。類要想使用,必須用類載入器載入,所以需要載入器。反射機制,不是每次都去重新反射,而是提供了cache,每次都會需要類載入器去自己的cache中尋找,如果可以查到,則直接返回該類。
有意思的是java的類載入器也有些門道。它分為BootStrap Class Loader(引導類載入器),Extensions Class Loader (擴充類載入器),App ClassLoader(或System
Class Loader),當然少不了Custom ClassLoader(使用者自訂類載入器)。其載入過程中會先檢查類是否被已載入,檢查順序是自底向上,從Custom ClassLoader到BootStrap ClassLoader逐層檢查,只要某個classloader已載入就視為已載入此類,保證此類只所有ClassLoader載入一次。而載入的順序是自頂向下,也就是由上層來逐層嘗試載入此類。類載入器的詳細介紹會在接下來的博文中較深入的分析,歡迎期待。
在forName方法中,調用了ClassLoader.loadClass方法來完成類的反射。根據類載入器的特殊性,結合我的調試過程,畫了一個簡單的流程圖,
我的這幅圖簡單的說明了類載入器的類載入過程。先檢查自己是否已經載入過該類,如果載入過,則直接返回該類,若沒有則調用父類的loadClass方法,如果父類中沒有,則執行findClass方法去嘗試載入此類,也就是我們通常所理解的片面的"反射"了。這個過程主要通過ClassLoader.defineClass方法來完成。defineClass 方法將一個位元組數群組轉換為
Class 類的執行個體(任何類的對象都是Class類的對象)。這種新定義的類的執行個體需要使用 Class.newInstance 來建立,而不能使用new來執行個體化。
為什麼說“任何類的對象都是Class類的對象”呢?在Java中,每個class都有一個相應的Class對象。也就是說,當我們編寫一個類(.java檔案),編譯完成後,在產生的.class檔案中,就會產生一個Class對象,用於表示這個類的類型資訊。
其實說的簡單通俗一點,就是在運行期間,如果我們要產生某個類的對象,Java虛擬機器(JVM)會檢查該類型的Class對象是否已被載入。如果沒有被載入,JVM會根據類的名稱找到.class檔案並載入它。一旦某個類型的Class對象已被載入到記憶體,就可以用它來產生該類型的所有對象。
以上內容是我經過調試、查java Api和網上資料,結合自己的理解,與大家分享討論的,如果有錯,歡迎大家指正,我們共同進步。