一:雙親委派機(jī)制
ClassLoader#loadClass(ClassLoader源碼)
二:類加載器
普通的類只需要有1、2、3加載器就夠了殿如,有特殊需求需要4莺治、5。
注:JVM調(diào)用BootstrapClassLoader啟動(dòng),然后BootstrapClassLoader構(gòu)造ExtClassLoader并啟動(dòng)它,加載擴(kuò)展類,構(gòu)造AppClassLoader公你,等到需要加載類路徑下的某類到內(nèi)存中才生效。即:JVM啟動(dòng)假瞬,BootstrapClassLoader和ExtClassLoader就會(huì)生效陕靠,AppClassLoader會(huì)等有類加載需求再生效,以上的加載都是自動(dòng)加載脱茉,不需要程序員去調(diào)loadClass()進(jìn)行加載剪芥。
1:BootStrapClassLoader
啟動(dòng)類加載器:加載的類庫(kù)位置是"C:\Program Files\Java\jdk1.8.0_151\jre\lib"。
1.1:c++編寫芦劣,已嵌入到了JVM內(nèi)核當(dāng)中粗俱。
1.2:如何通過(guò)Java的類加載器獲取到,ExtClassLoader的getParent()獲取到的是null(在類加載器部分:null就是指BootstrapClassLoader)虚吟。
/**
* 1:BootstrapClassLoader加載的類庫(kù)
* System.getProperty("sun.boot.class.path”)
*
* 結(jié)果:
* C:\Program Files\Java\jdk1.8.0_151\jre\lib\resources.jar;
* C:\Program Files\Java\jdk1.8.0_151\jre\lib\rt.jar;
* C:\Program Files\Java\jdk1.8.0_151\jre\lib\sunrsasign.jar;
* C:\Program Files\Java\jdk1.8.0_151\jre\lib\jsse.jar;
* C:\Program Files\Java\jdk1.8.0_151\jre\lib\jce.jar;
* C:\Program Files\Java\jdk1.8.0_151\jre\lib\charsets.jar;
* C:\Program Files\Java\jdk1.8.0_151\jre\lib\jfr.jar;
* C:\Program Files\Java\jdk1.8.0_151\jre\classes
*/
System.out.println(System.getProperty("sun.boot.class.path"));
2:ExtClassLoader
擴(kuò)展類加載器:加載的類庫(kù)位置是"C:\Program Files\Java\jdk1.8.0_151\jre\lib\ext".
2.1:java編寫寸认,位于sun.misc包下,該包在你導(dǎo)入源代碼的時(shí)候是沒(méi)有的串慰,需要重新去下偏塞,該包是openjdk的包,不公開(kāi)源代碼邦鲫。
/**
* 1:ExtClassLoader加載的類庫(kù)
* System.getProperty("java.ext.dirs")
*
* 結(jié)果:
* C:\Program Files\Java\jdk1.8.0_151\jre\lib\ext;
* C:\Windows\Sun\Java\lib\ext
*/
System.out.println(System.getProperty("java.ext.dirs"));
3:AppClassLoader
應(yīng)用程序類加載器:加載classpath下的class.
1:java編寫灸叼,位于sun.misc包下神汹,該包是openjdk的包,不公開(kāi)源代碼古今。
System.getProperty("java.class.path")的結(jié)果值就是AppClassLoader加載的類庫(kù)屁魏。
比較好的理解就是:idea上自己編寫的普通類就是AppClassLoader加載的。
4:CustomerClassLoader
自定義類加載器:自定義類加載器一般都是extends ClassLoader捉腥,然后重寫其findClass()方法氓拼,不要去重寫loadClass()方法,即不要去破壞其雙親委派的機(jī)制抵碟。
自定義類加載器的使用場(chǎng)景:
1:加密:Java代碼可以輕易的被反編譯桃漾,如果你需要把自己的代碼進(jìn)行加密以防止反編譯,可以先將編譯后的代碼用某種加密算法加密拟逮,類加密后就不能再用Java的ClassLoader去加載類了撬统,這時(shí)就需要自定義ClassLoader在加載類的時(shí)候先解密類,然后再加載敦迄。
2:從非標(biāo)準(zhǔn)的來(lái)源加載代碼:如果你的字節(jié)碼是放在數(shù)據(jù)庫(kù)恋追、甚至是在云端,就可以自定義類加載器颅崩,從指定的來(lái)源加載類几于。
1蕊苗、2的綜合運(yùn)用:比如你的應(yīng)用需要通過(guò)網(wǎng)絡(luò)來(lái)傳輸Java類的字節(jié)碼沿后,為了安全性,這些字節(jié)碼經(jīng)過(guò)了加密處理朽砰,這個(gè)時(shí)候你就需要自定義類加載器來(lái)從某個(gè)網(wǎng)絡(luò)地址上讀取加密后的字節(jié)代碼(findClass())尖滚,接著進(jìn)行解密和驗(yàn)證,最后定義(defineClass())出在Java虛擬機(jī)中運(yùn)行的類瞧柔。
5:ThreadContextClassLoader
線程上下文類加載器(TCCL):每一個(gè)線程都有一個(gè)關(guān)聯(lián)的ThreadContextClassLoader漆弄。
引入TCCL的原因:
Jvm的類加載機(jī)制是雙親委派機(jī)制,從父類加載器開(kāi)始加載再子類加載器加載造锅。若針對(duì)spi的接口和實(shí)現(xiàn)撼唾,spi的接口是核心類庫(kù),是由BootStrapClassLoader加載的哥蔚,BootStrapClassLoader是加載不到第三方j(luò)ar中的spi實(shí)現(xiàn)類的倒谷,因此引入線程上下文加載器,反向打通雙親委派糙箍,讓BootStrapClassLoader可以指定線程上下文加載器來(lái)加載spi的實(shí)現(xiàn)類渤愁。
注:線程上下文加載器若不設(shè)置,則默認(rèn)是AppClassLoader(該默認(rèn)的類加載器是Jvm設(shè)置的)深夯。
TCCL的作用:
1:解決委派雙親加載模式的缺點(diǎn)抖格。
2:實(shí)現(xiàn)了jndi,spi接口的加載。
TCCL的使用場(chǎng)景:
1:當(dāng)高層提供了統(tǒng)一接口讓低層去實(shí)現(xiàn)雹拄,同時(shí)又要是在高層加載(或?qū)嵗┑蛯拥念悤r(shí)收奔,必須通過(guò)線程上下文類加載器來(lái)幫助高層的ClassLoader找到并加載該類。參考:[https://blog.csdn.net/yangcheng33/article/details/52631940](https://blog.csdn.net/yangcheng33/article/details/52631940)
2:當(dāng)使用本類托管類加載滓玖,然而加載本類的ClassLoader未知時(shí)筹淫,為了隔離不同的調(diào)用者,可以取調(diào)用者各自的線程上下文類加載器代為托管呢撞。
Thread和TCCL的關(guān)系
new Thread()將繼承父線程的TCCL损姜。如果程序?qū)CCL沒(méi)有任何改動(dòng),則程序的所有線程將都使用AppClassLoader作為TCCL殊霞。
設(shè)置TCCL的方式:Thread.currentThread().setContextClassLoader("類加載器")
注:TCCL是線程隔離的摧阅,每個(gè)線程可以有不同的TCCL。
個(gè)人認(rèn)為绷蹲,TCCL可以和自定義類加載器一起使用棒卷,設(shè)置某一類線程的類加載器為自定義類加載器,專門做一些處理祝钢。
/**
* <p>
* TCCL
* </p>
* @author: zhu.chen
* @date: 2020/8/3
* @version: v1.0.0
*/
public class Test {
public static void main(String[] args) {
/**
* 1:JVM會(huì)為每個(gè)線程設(shè)置默認(rèn)TCCL為AppClassLoader
* 子線程t線程會(huì)繼承main的TCCL也為AppClassLoader
*/
// main:sun.misc.Launcher$AppClassLoader@18b4aac2
System.out.println(Thread.currentThread().getContextClassLoader());
Thread t = new Thread(() -> System.out.println("xxx"));
t.start();
// t:sun.misc.Launcher$AppClassLoader@18b4aac2
System.out.println(t.getContextClassLoader());
/**
* 2:主線程設(shè)置了TCCL為Test1
* 子線程t1繼承main的TCCL也為Test1(main和t的TCCL是一樣的)
*/
Thread.currentThread().setContextClassLoader(new Test1());
// com.example.demo2.Test$Test1@79fc0f2f
System.out.println(Thread.currentThread().getContextClassLoader());
Thread t1 = new Thread(() -> System.out.println("xxx"));
t1.start();
// com.example.demo2.Test$Test1@79fc0f2f
System.out.println(t1.getContextClassLoader());
}
/**
* 自定義類加載器
*/
public static class Test1 extends ClassLoader {
}
}
三:Java虛擬機(jī)是如何判定兩個(gè)Java類是相同的比规?
Java虛擬機(jī)不僅要看類的全名是否相同,還要看加載此類的類加載器是否一樣拦英,只有兩者都相同的情況蜒什,才認(rèn)為兩個(gè)類是相同的,即便是同樣的字節(jié)代碼疤估,被不同的類加載器加載之后所得到的類灾常,也是不同的。