前兩天頭條有朋友留言說使用class.forName找不到類,可以使用classloader加載。趁此機會總結一下,正好看到面試中還經常問到。
一、類加載機制
上面兩種加載類的方式說到底還是為了加載一個java類,因此需要先對類加載的過程進行一個簡單的瞭解。我們寫好的程序,然後run運行,過程可以直接看下面這張圖:
![你知道java反射機制中class.forName和classloader的區別嗎?](http://p2.ttnews.xyz/loading.gif)
往細了看大致分為5個階段:
(1)加載:java類運行時候會生成一個class字節碼文件,加載的過程就是去我們的操作系統尋找這個class文件。
(2)鏈接:這個過程就是把class文件加載到java虛擬機。
(3)初始化:在虛擬機中根據class文件進行初始化。
(4)使用:這個過程大家都明白。
(5)卸載:使用完了,java虛擬機進行清理。
對於class.forName和classloader來說針對的就是第一個過程,也就是加載過程。不過這倆雖然有一定的相似性,但是區別還是挺大的。
二、使用舉例
我們使用代碼,先看看如何使用。注意包的範圍,避免加載不了。
第一步:定義User類
![你知道java反射機制中class.forName和classloader的區別嗎?](http://p2.ttnews.xyz/loading.gif)
第二步:測試
我們在上面的test方法中,使用了兩個加載方法。現在我們測試一下:
是不感覺有點區別。現在是先給出一個大體的使用,下面我們分析一下他們的區別。
三、區別
1、class.forName
class.forName()前者除了將類的.class文件加載到jvm中之外,還會對類進行解釋,執行類中的static塊。注意這裡的靜態塊指的是在類初始化時的一些數據。但是classloader卻沒有,想要弄清楚這個原因,還是直接到源碼中看看。
在這個源碼中我們會發現,其實底層真正實現的是forName0方法,那這幾個參數又是什麼意思呢?
(1)className:表示我們要加載的類名
(2)true:指Class被加載後是不是必須被初始化。 不初始化就是不執行static的代碼即靜態代碼,在這裡默認為true,也就是默認實現類的初始化。
(3)ClassLoader.getClassLoader(caller):表示類加載器,到這你會發現forNanme其實也是使用的ClassLoader類加載器加載的。
(4)caller:指定類加載器。
所以,在這裡你可以指定是否在class加載後被初始化。而且底層還是使用的classloader加載的。
2、classloader
在上面的案例中我們發現,classloader並沒有初始化靜態塊,原因最好還是到源碼中看。
首先我們先進入到loadclass方法中的源碼。
<code>publicClass>loadClass(Stringname)
throwsClassNotFoundException{
returnloadClass(name,false);
}/<code>
這一步看起來還看不明白,沒關係這裡真正實現的是內部的loadclass,我們再跟進去看看。
這個才是真正實現的方法,在這裡的步驟其實很簡單,大致流程是先判斷class是否已經被加載,如果被加載了那就重新加載,如果沒有加載那就使用雙親委派原則加載。加載的時候並沒有指定是否要進行初始化。
所以現在他們的區別基本上很少,總結一下:
(1)class.forName()除了將類的.class文件加載到jvm中之外,還會對類進行解釋,執行類中的static塊。當然還可以指定是否執行靜態塊。
(2)classLoader只幹一件事情,就是將.class文件加載到jvm中,不會執行static中的內容,只有在newInstance才會去執行static塊。
有一個小問題需要注意:我在網上看了幾篇文章,親測有錯誤,那就是class.forName其實是不會執行靜態方法的,但是會初始化靜態變量。錯誤的例子是使用了靜態方法為靜態變量賦值了。
ok,一個小知識點。如有問題,還請批評指正。
閱讀更多 Java的架構師技術棧 的文章