封面來源:微信公衆號 雜亂無章--樹熊
java
受多種狀況的影響,又開始看JVM 方面的知識。面試
一、Java 實在過於內卷,無法不往深了學。sql
二、面試題問的多,被迫學習。api
三、純粹的好奇。 很喜歡一句話:「八小時內謀生活,八小時外謀發展。」 --- 望別日與君相見時,君已有所成。
共勉
微信
Java類加載器(英語:Java Classloader)是Java運行時環境(Java Runtime Environment)的一部分,負責動態加載Java類到Java虛擬機的內存空間中。類一般是按需加載,即第一次使用該類時才加載。因爲有了類加載器,Java運行時系統不須要知道文件與文件系統。markdown
ClassLoader只負責class文件的加載,至於它是否能夠運行,則由Execution Engine(執行引擎)決定。ide
==每一個Java類必須由某個類加載器裝入到內存。==oop
圖示:學習
加載的類信息存放於一塊稱爲方法區的內存空間。除了類的信息外,方法區中還會存放運行時常量池信息,可能還包括字符串字面量和數字常量(這部分常量信息是Class文件中常量池部分的內存映射)測試
JVM支持兩種類型的類加載器 。分別爲引導類加載器(Bootstrap ClassLoader)和自定義類加載器(User-Defined ClassLoader) 從概念上來說,自定義類加載器通常指的是程序中由開發人員自定義的一類類加載器,可是Java虛擬機規範卻沒有這麼定義,而是將全部派生於抽象類ClassLoader的類加載器都劃分爲自定義類加載器
類加載器結構圖:
又可稱之爲 根類加載器、引導類加載器、啓動類加載器,看到這些都要知道說的是 ==Bootstrap ClassLoader==。
此類加載器是Java虛擬機的一部分,使用native代碼(C++)編寫。 因此它是獲取不到的
加載位於/jre/lib目錄中的或者被參數-Xbootclasspath所指定的目錄下的核心Java類庫。
如圖:
如上圖:用來加載Java的核心庫
,rt.jar這個jar包就是Bootstrap根類加載器負責加載的,其中包含了java各類核心的類如java.lang,java.io,java.util,java.sql等 ,
Bootstrap ClassLoader 並不繼承自java.lang.ClassLoader
,沒有父加載器
加載擴展類和應用程序類加載器,並做爲他們的父類加載器(當他倆的爹)
Bootstrap啓動類加載器只加載包名爲java、javax、sun
等開頭的類
代碼測試:
public class BootstrapClassLoaderTest {
public static void main(String[] args) {
String s = new String();
ClassLoader loader = s.getClass().getClassLoader();
System.out.println("讓咱們一塊兒來看看 String 是由加載的吧 ==>"+loader);
//Bootstrap ClassLoader ==>null
Integer integer = 2;
ClassLoader classLoader1 = integer.getClass().getClassLoader();
System.out.println("讓咱們一塊兒來看看 Integer 是由加載的吧 "+classLoader1);
//Bootstrap ClassLoader ==>null
}
}
複製代碼
經過測試,咱們能夠發現所有都是 null ,其實 BootstrapClassLoader 咱們是獲取不到,它是由C++編寫的。
測試小結:
咱們能夠證實,java、javax、sun
等開頭的類 的類確實是由啓動類加載器加載的。 其餘的你們也均可以試一試。
你們也會想,若是咱們建項目時,也建立 一個java.lang 包,而後在底下寫一個String類 或者寫一個自定義類(MyUser)。啓動類加載器會加載它嗎?
答案:是什麼???? 見文末。
環境要jdk 8 。 個人環境是 jdk11。因此底下代碼是從 blog.csdn.net/sj158149630… 搬過來的。
代碼測試:
public class ClassLoaderTest1 {
public static void main(String[] args) {
System.out.println("**********啓動類加載器**************");
//獲取BootstrapClassLoader可以加載的api的路徑
URL[] urLs = sun.misc.Launcher.getBootstrapClassPath().getURLs();
for (URL element : urLs) {
System.out.println(element.toExternalForm());
}
//從上面的路徑中隨意選擇一個類,來看看他的類加載器是什麼:引導類加載器
ClassLoader classLoader = Provider.class.getClassLoader();
System.out.println(classLoader); //null
System.out.println("***********擴展類加載器*************");
String extDirs = System.getProperty("java.ext.dirs");
for (String path : extDirs.split(";")) {
System.out.println(path);
}
//從上面的路徑中隨意選擇一個類,來看看他的類加載器是什麼:擴展類加載器
ClassLoader classLoader1 = CurveDB.class.getClassLoader();
System.out.println(classLoader1);//sun.misc.Launcher$ExtClassLoader@1540e19d
}
}
複製代碼
System.out.println(classLoader); //null 再次證實咱們沒法獲取到啓動類加載器
**********啓動類加載器**************
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/jre/lib/resources.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/jre/lib/rt.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/jre/lib/sunrsasign.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/jre/lib/jsse.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/jre/lib/jce.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/jre/lib/charsets.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/jre/lib/jfr.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/jre/classes
null
***********擴展類加載器*************
/Users/xiexu/Library/Java/Extensions:/Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/jre/lib/ext:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java
sun.misc.Launcher$ExtClassLoader@d716361
複製代碼
該類加載是程序中默認的類加載器
,通常來講,Java應用的類都是由它來完成加載經過classLoader.getSystemclassLoader()
方法能夠獲取到該類加載器代碼測試:
public class AppClassLoaderTest {
public static void main(String[] args) {
BootstrapClassLoaderTest loaderTest = new BootstrapClassLoaderTest();
ClassLoader classLoader = loaderTest.getClass().getClassLoader();
System.out.println(classLoader);
//jdk.internal.loader.ClassLoaders$AppClassLoader@2437c6dc
}
}
複製代碼
==小結==:因此通常來講,Java應用的類都是由它來完成加載
能夠經過繼承java.lang.ClassLoader類的方式實現本身的類加載器,以知足一些特殊的需求而不須要徹底瞭解Java虛擬機的類加載的細節。
可用於:
運行時裝載或卸載類。這經常使用於:
改變Java字節碼的裝入,例如,可用於Java類字節碼的加密裝入。)
修改已裝入的字節碼weavingof aspects when usingaspect-oriented programming)。
==ClassLoader類,它是一個抽象類
,其後全部的類加載器都繼承自ClassLoader(不包括啓動類加載器
)==
如何獲取到ClassLoader 呢?
總共有下面四種方式:
一、獲取當前類的Classloader
二、獲取當前線程上下文的ClassLoader
三、獲取系統的ClassLoader
四、獲取調用者的ClassLoader DriverManager.getCallerClassLoader()
public static void main(String[] args) {
//一、獲取當前類的ClassLoader
BootstrapClassLoaderTest classLoaderTest = new BootstrapClassLoaderTest();
ClassLoader classLoader = classLoaderTest.getClass().getClassLoader();
System.out.println(classLoader);
//二、獲取當前線程上下文的ClassLoader
new Thread(){
@Override
public void run() {
System.out.println(Thread.currentThread());
ClassLoader classLoader1 = Thread.currentThread().getContextClassLoader();
System.out.println(classLoader);
}
}.run();
//三、獲取系統的ClassLoader
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);
}
複製代碼
下圖來自於:圖
我也親自去嘗試了,所有都是報錯。直接報 關於包的錯誤。創立String 類型也是同樣的。
代碼及結構:
錯誤:1、報的直接是說 此java程序包 已存在另外一個模塊中 。2、報啓動類加載器 找不到。
天天的焦慮生活,讓人無比疲憊,卻又迫不得已。