public class MyObj { static { System.out.println("MyObj class init."); } }
public class Test implements Cloneable, Serializable { public static void main(String[] args) throws Exception { Class.forName("com.test.MyObj"); // ClassLoader.getSystemClassLoader().loadClass("com.test.MyObj"); } }
Class.forName 和 ClassLoader.loadClass 的差別
類別載入
為了弄清楚
Class.forName
和ClassLoader.loadClass
的區別, 首先我們需要了解 JVM 中類加載的步驟.類的加載可以分為如下幾步
載入: 透過類別的全限定名取得到類別的二進位流, 然後載入到 JVM 中
驗證: 確保Class 檔案的位元組流中包含的資訊符合虛擬機器的要求, 並且不會危害虛擬機器的安全
準備: 為類別變數分配記憶體空間並設定類變初始值
解析
初始化: 根據使用者指定的程式碼初始化欄位和其他資源, 執行 static 區塊.
Class.forName
當我們通過:
來取得一個Class 時, 那麼其實相當於呼叫了
Class.forName(className, true, currentLoader)
, 這個方法的第二個參數表示是否需要初始化類別. 我們設定為true
, 因此Class.forName 取得到Class 物件時, 會自動對類別進行初始化的.Class.forName(className, true, currentLoader)
, 这个方法的第二个参数表示是否需要初始化类. 我们设置为true
, 因此 Class.forName 获取到 Class 对象时, 会自动对类进行初始化的.并且 Class.forName 加载类的 ClassLoader 和调用
Class.forName
並且Class.forName 載入類別的ClassLoader 和呼叫Class.forName
所在的類別的ClassLoader相同.ClassLoader.loadClass
與
Class.forName
不同, 預設情況下ClassLoader.loadClass
並不會初始化類別, 即類別載入的初始化
步驟沒有執行, 因此類別中的靜態程式碼區塊不會執行.Class.forName
不同, 默认情况下ClassLoader.loadClass
并不会初始化类, 即类加载的初始化
步骤没有执行, 因此类中的静态代码块不会执行.并且使用
ClassLoader.loadClass
並且使用ClassLoader.loadClass
時, 我們可以指定不同的ClassLoader. 例如:一個例子
那麼上面的程式碼中,
Class.forName("com.test.MyObj")
的调用会触发 MyObj 的静态代码块的执行, 而ClassLoader.getSystemClassLoader().loadClass("com.test.MyObj");
並不會.這樣使用有什麼好處?
我個人猜測, 應該和Spring IoC 的Lazy loading 有關, Spring IoC 為了加快初始化速度, 因此大量使用了延時加載技術. 而使用classloader 不需要執行類中的初始化代碼, 可以加快加載速度, 把類的初始化工作留到實際使用到這個類別的時候.