Class.forName 和 ClassLoader 到底有啥區(qū)別?

作者 |?紀(jì)莫
前言
最近在面試過(guò)程中有被問(wèn)到,在Java反射中Class.forName()加載類(lèi)和使用ClassLoader加載類(lèi)的區(qū)別。當(dāng)時(shí)沒(méi)有想出來(lái)后來(lái)自己研究了一下就寫(xiě)下來(lái)記錄一下。
解釋
在java中Class.forName()和ClassLoader都可以對(duì)類(lèi)進(jìn)行加載。ClassLoader就是遵循雙親委派模型最終調(diào)用啟動(dòng)類(lèi)加載器的類(lèi)加載器,實(shí)現(xiàn)的功能是“通過(guò)一個(gè)類(lèi)的全限定名來(lái)獲取描述此類(lèi)的二進(jìn)制字節(jié)流”,獲取到二進(jìn)制流后放到JVM中。Class.forName()方法實(shí)際上也是調(diào)用的ClassLoader來(lái)實(shí)現(xiàn)的。
Class.forName(String className);這個(gè)方法的源碼是
@CallerSensitive
public?static?Class>?forName(String?className)
????????????throws?ClassNotFoundException?{
????Class>?caller?=?Reflection.getCallerClass();
????return?forName0(className,?true,?ClassLoader.getClassLoader(caller),?caller);
}
最后調(diào)用的方法是forName0這個(gè)方法,在這個(gè)forName0方法中的第二個(gè)參數(shù)被默認(rèn)設(shè)置為了true,這個(gè)參數(shù)代表是否對(duì)加載的類(lèi)進(jìn)行初始化,設(shè)置為true時(shí)會(huì)類(lèi)進(jìn)行初始化,代表會(huì)執(zhí)行類(lèi)中的靜態(tài)代碼塊,以及對(duì)靜態(tài)變量的賦值等操作。
也可以調(diào)用Class.forName(String name, boolean initialize,ClassLoader loader)方法來(lái)手動(dòng)選擇在加載類(lèi)的時(shí)候是否要對(duì)類(lèi)進(jìn)行初始化。Class.forName(String name, boolean initialize,ClassLoader loader)的源碼如下:
/*?@param?name???????fully?qualified?name?of?the?desired?class
?*?@param?initialize?if?{@code?true}?the?class?will?be?initialized.
?*???????????????????See?Section?12.4?of?The?Java?Language?Specification.
?*?@param?loader?????class?loader?from?which?the?class?must?be?loaded
?*?@return???????????class?object?representing?the?desired?class
?*
?*?@exception?LinkageError?if?the?linkage?fails
?*?@exception?ExceptionInInitializerError?if?the?initialization?provoked
?*????????????by?this?method?fails
?*?@exception?ClassNotFoundException?if?the?class?cannot?be?located?by
?*????????????the?specified?class?loader
?*
?*?@see???????java.lang.Class#forName(String)
?*?@see???????java.lang.ClassLoader
?*?@since?????1.2?????*/
@CallerSensitive
public?static?Class>?forName(String?name,?boolean?initialize,
???????????????????????????????ClassLoader?loader)
????throws?ClassNotFoundException
{
????Class>?caller?=?null;
????SecurityManager?sm?=?System.getSecurityManager();
????if?(sm?!=?null)?{
????????//?Reflective?call?to?get?caller?class?is?only?needed?if?a?security?manager
????????//?is?present.??Avoid?the?overhead?of?making?this?call?otherwise.
????????caller?=?Reflection.getCallerClass();
????????if?(sun.misc.VM.isSystemDomainLoader(loader))?{
????????????ClassLoader?ccl?=?ClassLoader.getClassLoader(caller);
????????????if?(!sun.misc.VM.isSystemDomainLoader(ccl))?{
????????????????sm.checkPermission(
????????????????????SecurityConstants.GET\_CLASSLOADER\_PERMISSION);
????????????}
????????}
????}
????return?forName0(name,?initialize,?loader,?caller);
}
源碼中的注釋只摘取了一部分,其中對(duì)參數(shù)initialize的描述是:if {@code true} the class will be initialized.意思就是說(shuō):如果參數(shù)為true,則加載的類(lèi)將會(huì)被初始化。
舉例
下面還是舉例來(lái)說(shuō)明結(jié)果吧:
一個(gè)含有靜態(tài)代碼塊、靜態(tài)變量、賦值給靜態(tài)變量的靜態(tài)方法的類(lèi)
public?class?ClassForName?{
????//靜態(tài)代碼塊
????static?{
????????System.out.println("執(zhí)行了靜態(tài)代碼塊");
????}
????//靜態(tài)變量
????private?static?String?staticFiled?=?staticMethod();
????//賦值靜態(tài)變量的靜態(tài)方法
????public?static?String?staticMethod(){
????????System.out.println("執(zhí)行了靜態(tài)方法");
????????return?"給靜態(tài)字段賦值了";
????}
}
使用Class.forName()的測(cè)試方法:
@Test
public?void?test45(){
????try?{
????????ClassLoader.getSystemClassLoader().loadClass("com.eurekaclient2.client2.ClassForName");
????????System.out.println("#########-------------結(jié)束符------------##########");
????}?catch?(ClassNotFoundException?e)?{
????????e.printStackTrace();
????}
}
運(yùn)行結(jié)果:
執(zhí)行了靜態(tài)代碼塊執(zhí)行了靜態(tài)方法#########-------------結(jié)束符------------##########??
使用ClassLoader的測(cè)試方法:
@Test
public?void?test45(){
????try?{
????????ClassLoader.getSystemClassLoader().loadClass("com.eurekaclient2.client2.ClassForName");
????????System.out.println("#########-------------結(jié)束符------------##########");
????}?catch?(ClassNotFoundException?e)?{
????????e.printStackTrace();
????}
}
運(yùn)行結(jié)果:
#########-------------結(jié)束符------------##########
根據(jù)運(yùn)行結(jié)果得出Class.forName加載類(lèi)時(shí)將類(lèi)進(jìn)了初始化,而ClassLoader的loadClass并沒(méi)有對(duì)類(lèi)進(jìn)行初始化,只是把類(lèi)加載到了虛擬機(jī)中。
應(yīng)用場(chǎng)景
在我們熟悉的Spring框架中的IOC的實(shí)現(xiàn)就是使用的ClassLoader。
而在我們使用JDBC時(shí)通常是使用Class.forName()方法來(lái)加載數(shù)據(jù)庫(kù)連接驅(qū)動(dòng)。這是因?yàn)樵贘DBC規(guī)范中明確要求Driver(數(shù)據(jù)庫(kù)驅(qū)動(dòng))類(lèi)必須向DriverManager注冊(cè)自己。
以MySQL的驅(qū)動(dòng)為例解釋?zhuān)?/p>
public?class?Driver?extends?NonRegisteringDriver?implements?java.sql.Driver?{??
????//?~?Static?fields/initializers??
????//?---------------------------------------------??
????//??
????//?Register?ourselves?with?the?DriverManager??
????//??
????static?{??
????????try?{??
????????????java.sql.DriverManager.registerDriver(new?Driver());??
????????}?catch?(SQLException?E)?{??
????????????throw?new?RuntimeException("Can't?register?driver!");??
????????}??
????}??
????//?~?Constructors??
????//?-----------------------------------------------------------??
????/**?
?????*?Construct?a?new?driver?and?register?it?with?DriverManager?
?????*??
?????*?@throws?SQLException?
?????*?????????????if?a?database?error?occurs.?
?????*/??
????public?Driver()?throws?SQLException?{??
????????//?Required?for?Class.forName().newInstance()??????}??
}
我們看到Driver注冊(cè)到DriverManager中的操作寫(xiě)在了靜態(tài)代碼塊中,這就是為什么在寫(xiě)JDBC時(shí)使用Class.forName()的原因了。關(guān)注微信公眾號(hào):Java技術(shù)棧,在后臺(tái)回復(fù):java,可以獲取我整理的 N 篇最新 Java?教程,都是干貨。
好了,今天就寫(xiě)到這了,最近在面試,遇到了很多問(wèn)題,也學(xué)習(xí)了不少,雖然很累,但是也讓人成長(zhǎng)了不少,畢竟面試就是一個(gè)脫皮的過(guò)程,會(huì)遇到各種企業(yè)各種面試官各種問(wèn)題,各種場(chǎng)景。
給自己加油吧,找一個(gè)最少能讓自己干個(gè)幾年的公司,別總是讓我遇到工作了沒(méi)多久公司就垮掉的這種就行了。要不我也很無(wú)奈啊。
等找到工作后,會(huì)總結(jié)自己面經(jīng)的。
- 推薦閱讀 -
《架構(gòu)師離職后,成為自由開(kāi)發(fā)者的第 100 天》
下方二維碼關(guān)注我

互聯(lián)網(wǎng)草根,堅(jiān)持分享技術(shù)、創(chuàng)業(yè)、產(chǎn)品等心得和總結(jié)~

點(diǎn)擊“閱讀原文”,領(lǐng)取 2020 年最新免費(fèi)技術(shù)資料大全
