在理解這兩種反射機制之前,需要弄清楚java類的加載機制.
裝載:通過類的全限定名獲取二進制字節流(二進制的class文件),將二進制字節流轉換成方法區中的運行時數據結構,在內存中生成Java.lang.class對象。這個時候該類型沒有被分配內存,設置默認值,也沒有初始化。
鏈接:執行下面的校驗、准備和解析步驟,其中解析步驟是可以選擇的;
校驗:檢查導入類或接口的二進制數據的正確性;(文件格式驗證,元數據驗證,字節碼驗證,符號引用驗證)
准備:給類的靜態變量分配並初始化存儲空間;
解析:將常量池中的符號引用轉成直接引用;
初始化:激活類的靜態變量的初始化Java代碼和靜態Java代碼塊,並初始化程序員設置的變量值。
Class.forName有兩個重載方法:
public static Class<?> forName(String name, boolean initialize, ClassLoader loader) throws ClassNotFoundException public static Class<?> forName(String className) throws ClassNotFoundException
解釋:
name:類的全限定名,如:com.org.prj
initialize:如果為true,則會在返回Class對象之前,對該類型做連接,校驗,初始化操作。(如:執行static塊中的代碼),initialize默認需要初始化。
loader:用自定義的類加載器來請求這個類型;當然,你也可以傳入null,用bootstrap加載器
由於Class.forName默認是需要初始化,一旦初始化,就會觸發目標對象的 static塊代碼執行,static參數也也會被再次初始化。
我們在來看ClassLoader.loadClass也有兩個兩個重載方法:
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException public Class<?> loadClass(String name) throws ClassNotFoundException
解釋:
name:類的全限定名,如:com.org.prj
resolve:表示是否需要連接該類型。 僅僅是連接(這里面包括校驗class文件,准備分配內存,類型常量池的替換),並不會初始化該類型。
resolve默認是不鏈接,不進行鏈接意味着不進行包括初始化等一些列步驟,那么靜態塊和靜態對象就不會得到執行。
總結:
1.Class.forName返回的Class對象可以決定是否初始化。而ClassLoader.loadClass返回的類型絕對不會初始化,最多只會做連接操作。
2.Class.forName可以決定由哪個classLoader來請求這個類型。而ClassLoader.loadClass是用當前的classLoader去請求。