JVM:第一章:類加載機制深度解析
Java 源代碼執(zhí)行流程
解釋:
啟動虛擬機 (C++負責創(chuàng)建) 【windows : bin/java.exe調(diào)用 jvm.dll Linux : java 調(diào)用 libjvm.so 】
創(chuàng)建一個引導(dǎo)類加載器實例 (C++實現(xiàn))
C++ 調(diào)用Java代碼,創(chuàng)建JVM啟動器,實例sun.misc.Launcher 【這貨由引導(dǎo)加載器負責加載創(chuàng)建其他類加載器】
sun.misc.Launcher.getLauncher() 獲取運行類自己的加載器ClassLoader --> 是AppClassLoader
獲取到ClassLoader后調(diào)用loadClass(“A”)方法加載運行的類A
加載完成執(zhí)行A類的main方法
程序運行結(jié)束
JVM銷毀
loadClass的類加載過程
加載 ----> 驗證 ----> 準備 ----> 解析 ----> 初始化 ----> 使用 ----> 卸載
談及比較多的是前五個
加載:我們說jvm執(zhí)行的java字節(jié)碼,編譯后在磁盤上,總得讀取這個字節(jié)碼文件吧 ,通過啥讀 IO唄 , 所以第一步肯定是加載字節(jié)碼文件。
驗證:JVM總不能說讀到啥就直接運行了吧,你外面有個A.class 里面是一堆JVM規(guī)范不認識的內(nèi)容,也執(zhí)行不了啊 。 符合JVM規(guī)范才能執(zhí)行后續(xù)的步驟,所以第二步是 校驗字節(jié)碼文件的正確性。
準備:給類的靜態(tài)變量分配內(nèi)存,并賦予默認值。 我們的類里,可能會包含一些靜態(tài)變量吧 。 比如 public static final int a = 12; 得給a分配個默認值 0 ,再比如 public static User user = new User(); 給 static的變量 User分配內(nèi)存,并賦默認值null (final修飾的常量,直接賦值)。
解析:這個地方不是很好理解, 解析是什么意思呢?將符號引用替換為直接引用。 符號引用 ? 直接引用? what ? 我們的類的靜態(tài)方法,比如main方法,其實在Java中有個叫法 都是叫符號 。 這個階段就會吧,一些靜態(tài)方法(符號引用,比如剛才說的main方法)替換為指向數(shù)據(jù)所存內(nèi)存的指針或者句柄等(直接引用)【找到具體在內(nèi)存中的位置】。 這個就是靜態(tài)鏈接過程(在類加載期間完成)。 動態(tài)鏈接是在程序運行期間完成的將符號引用替換為直接引用 (比如某個普通方法的調(diào)用)。
初始化:上面的步驟完事兒以后,這一步主要是對類的靜態(tài)變量初始化為指定的值,執(zhí)行靜態(tài)代碼塊。 比如剛才第二步的 public static final int a = 12; ,第二步給static變量賦了默認值,這一步就該把12賦值給它了。 還有 static的 User public static User user = new User(); 實例化User。
說明:主類在運行過程中如果使用到其它類,會逐步加載這些類。jar包或war包里的類不是一次性全部加載的,是使用到時才加載。
類加載順序示例:
public class TestDynamicLoad {
static {
System.out.println("加載TestDynamicLoad類中的靜態(tài)代碼塊");
}
public static void main(String[] args) {
new A();
System.out.println("執(zhí)行main方法中的代碼");
B b = null;//B不會加載,除非這里執(zhí)行 new B()
}
}
class A{
static {
System.out.println("加載A類中的靜態(tài)代碼塊");
}
public A(){
System.out.println("加載A類中的構(gòu)造方法");
}
}
class B{
static {
System.out.println("加載B類中的靜態(tài)代碼塊");
}
public B(){
System.out.println("加載B類中的構(gòu)造方法");
}
}
執(zhí)行結(jié)果:
加載TestDynamicLoad類中的靜態(tài)代碼塊
加載A類中的靜態(tài)代碼塊
加載A類中的構(gòu)造方法
執(zhí)行main方法中的代碼
類加載器
引導(dǎo)類加載器:負責加載支撐JVM運行的位于JRE的lib目錄下的核心類庫,比如rt.jar、charsets.jar等
擴展類加載器:負責加載支撐JVM運行的位于JRE的lib目錄下的ext擴展目錄中的JAR類包
應(yīng)用程序類加載器:負責加載ClassPath路徑下的類包,主要就是加載我們應(yīng)用中自己寫的那些類
自定義加載器:負責加載用戶自定義路徑下的類包
類加載器示例:
import com.sun.crypto.provider.DESKeyFactory;
import sun.misc.Launcher;
import java.net.URL;
public class TestJDKClassLoader {
public static void main(String[] args) {
System.out.println(String.class.getClassLoader());
System.out.println(DESKeyFactory.class.getClassLoader().getClass().getName());
System.out.println(TestJDKClassLoader.class.getClassLoader().getClass().getName());
System.out.println();
ClassLoader appClassLoader = ClassLoader.getSystemClassLoader();
ClassLoader extClassloader = appClassLoader.getParent();
ClassLoader bootstrapLoader = extClassloader.getParent();
System.out.println("the bootstrapLoader : " + bootstrapLoader);
System.out.println("the extClassloader : " + extClassloader);
System.out.println("the appClassLoader : " + appClassLoader);
System.out.println("AppClassLoader的父類加載器為ExtClassLoader,ExtClassLoader的父類加載器為null,null并不代表ExtClassLoader沒有父類加載器,而是
BootstrapClassLoader");
System.out.println();
System.out.println("JRE的lib目錄下的核心類庫,bootstrapLoader加載以下文件:");
URL[] urls = Launcher.getBootstrapClassPath().getURLs();
for (int i = 0; i < urls.length; i++) {
System.out.println(urls[i]);
}
System.out.println();
System.out.println("JRE的lib目錄下的ext擴展目錄中的JAR類包,extClassloader加載以下文件:");
String[] extClassloaderStr = System.getProperty("java.ext.dirs").split(";");
for (String s : extClassloaderStr) {
System.out.println(s);
}
System.out.println();
System.out.println("加載ClassPath路徑下的類包,appClassLoader加載以下文件:");
String[] split = System.getProperty("java.class.path").split(";");
for (String s : split) {
System.out.println(s);
}
}
}
執(zhí)行結(jié)果:
null
sun.misc.Launcher$ExtClassLoader
sun.misc.Launcher$AppClassLoader
the bootstrapLoader : null
the extClassloader : sun.misc.Launcher$ExtClassLoader@3dd3bcd
the appClassLoader : sun.misc.Launcher$AppClassLoader@14dad5dc
AppClassLoader的父類加載器為ExtClassLoader,ExtClassLoader的父類加載器為null,null并不代表ExtClassLoader沒有父類加載器,而是 BootstrapClassLoader
JRE的lib目錄下的核心類庫,bootstrapLoader加載以下文件:
file:/D:/Environment/JDK/lib/resources.jar
file:/D:/Environment/JDK/lib/rt.jar
file:/D:/Environment/JDK/lib/sunrsasign.jar
file:/D:/Environment/JDK/lib/jsse.jar
file:/D:/Environment/JDK/lib/jce.jar
file:/D:/Environment/JDK/lib/charsets.jar
file:/D:/Environment/JDK/lib/jfr.jar
file:/D:/Environment/JDK/classes
JRE的lib目錄下的ext擴展目錄中的JAR類包,extClassloader加載以下文件:
D:\Environment\JDK\lib\ext
C:\Windows\Sun\Java\lib\ext
加載ClassPath路徑下的類包,appClassLoader加載以下文件:
D:\Environment\JDK\jre\lib\charsets.jar
D:\Environment\JDK\jre\lib\deploy.jar
D:\Environment\JDK\jre\lib\ext\access-bridge-64.jar
D:\Environment\JDK\jre\lib\ext\cldrdata.jar
D:\Environment\JDK\jre\lib\ext\dnsns.jar
D:\Environment\JDK\jre\lib\ext\jaccess.jar
D:\Environment\JDK\jre\lib\ext\jfxrt.jar
D:\Environment\JDK\jre\lib\ext\localedata.jar
D:\Environment\JDK\jre\lib\ext\nashorn.jar
D:\Environment\JDK\jre\lib\ext\sunec.jar
D:\Environment\JDK\jre\lib\ext\sunjce_provider.jar
D:\Environment\JDK\jre\lib\ext\sunmscapi.jar
D:\Environment\JDK\jre\lib\ext\sunpkcs11.jar
D:\Environment\JDK\jre\lib\ext\zipfs.jar
D:\Environment\JDK\jre\lib\javaws.jar
D:\Environment\JDK\jre\lib\jce.jar
D:\Environment\JDK\jre\lib\jfr.jar
D:\Environment\JDK\jre\lib\jfxswt.jar
D:\Environment\JDK\jre\lib\jsse.jar
D:\Environment\JDK\jre\lib\management-agent.jar
D:\Environment\JDK\jre\lib\plugin.jar
D:\Environment\JDK\jre\lib\resources.jar
D:\Environment\JDK\jre\lib\rt.jar
D:\Project\Personal\website\reception\target\classes
D:\Environment\RepMaven\org\springframework\boot\spring-boot-starter\2.2.6.RELEASE\spring-boot-starter-2.2.6.RELEASE.jar
D:\Environment\RepMaven\org\springframework\boot\spring-boot\2.2.6.RELEASE\spring-boot-2.2.6.RELEASE.jar
D:\Environment\RepMaven\org\springframework\spring-context\5.2.5.RELEASE\spring-context-5.2.5.RELEASE.jar
D:\Environment\RepMaven\org\springframework\boot\spring-boot-autoconfigure\2.2.6.RELEASE\spring-boot-autoconfigure-2.2.6.RELEASE.jar
D:\Environment\RepMaven\org\springframework\boot\spring-boot-starter-logging\2.2.6.RELEASE\spring-boot-starter-logging-2.2.6.RELEASE.jar
D:\Environment\RepMaven\ch\qos\logback\logback-classic\1.2.3\logback-classic-1.2.3.jar
D:\Environment\RepMaven\ch\qos\logback\logback-core\1.2.3\logback-core-1.2.3.jar
D:\Environment\RepMaven\org\apache\logging\log4j\log4j-to-slf4j\2.12.1\log4j-to-slf4j-2.12.1.jar
D:\Environment\RepMaven\org\apache\logging\log4j\log4j-api\2.12.1\log4j-api-2.12.1.jar
D:\Environment\RepMaven\org\slf4j\jul-to-slf4j\1.7.30\jul-to-slf4j-1.7.30.jar
D:\Environment\RepMaven\jakarta\annotation\jakarta.annotation-api\1.3.5\jakarta.annotation-api-1.3.5.jar
D:\Environment\RepMaven\org\springframework\spring-core\5.2.5.RELEASE\spring-core-5.2.5.RELEASE.jar
D:\Environment\RepMaven\org\springframework\spring-jcl\5.2.5.RELEASE\spring-jcl-5.2.5.RELEASE.jar
D:\Environment\RepMaven\org\yaml\snakeyaml\1.25\snakeyaml-1.25.jar
D:\Environment\RepMaven\org\springframework\boot\spring-boot-starter-web\2.2.6.RELEASE\spring-boot-starter-web-2.2.6.RELEASE.jar
D:\Environment\RepMaven\org\springframework\boot\spring-boot-starter-json\2.2.6.RELEASE\spring-boot-starter-json-2.2.6.RELEASE.jar
D:\Environment\RepMaven\com\fasterxml\jackson\core\jackson-databind\2.10.3\jackson-databind-2.10.3.jar
D:\Environment\RepMaven\com\fasterxml\jackson\core\jackson-core\2.10.3\jackson-core-2.10.3.jar
D:\Environment\RepMaven\com\fasterxml\jackson\datatype\jackson-datatype-jdk8\2.10.3\jackson-datatype-jdk8-2.10.3.jar
D:\Environment\RepMaven\com\fasterxml\jackson\datatype\jackson-datatype-jsr310\2.10.3\jackson-datatype-jsr310-2.10.3.jar
D:\Environment\RepMaven\com\fasterxml\jackson\module\jackson-module-parameter-names\2.10.3\jackson-module-parameter-names-2.10.3.jar
D:\Environment\RepMaven\org\springframework\boot\spring-boot-starter-validation\2.2.6.RELEASE\spring-boot-starter-validation-2.2.6.RELEASE.jar
D:\Environment\RepMaven\jakarta\validation\jakarta.validation-api\2.0.2\jakarta.validation-api-2.0.2.jar
D:\Environment\RepMaven\org\hibernate\validator\hibernate-validator\6.0.18.Final\hibernate-validator-6.0.18.Final.jar
D:\Environment\RepMaven\org\jboss\logging\jboss-logging\3.4.1.Final\jboss-logging-3.4.1.Final.jar
D:\Environment\RepMaven\org\springframework\spring-web\5.2.5.RELEASE\spring-web-5.2.5.RELEASE.jar
D:\Environment\RepMaven\org\springframework\spring-beans\5.2.5.RELEASE\spring-beans-5.2.5.RELEASE.jar
D:\Environment\RepMaven\org\springframework\spring-webmvc\5.2.5.RELEASE\spring-webmvc-5.2.5.RELEASE.jar
D:\Environment\RepMaven\org\springframework\spring-aop\5.2.5.RELEASE\spring-aop-5.2.5.RELEASE.jar
D:\Environment\RepMaven\org\springframework\spring-expression\5.2.5.RELEASE\spring-expression-5.2.5.RELEASE.jar
D:\Environment\RepMaven\org\apache\tomcat\embed\tomcat-embed-core\9.0.33\tomcat-embed-core-9.0.33.jar
D:\Environment\RepMaven\org\apache\tomcat\embed\tomcat-embed-el\9.0.33\tomcat-embed-el-9.0.33.jar
D:\Environment\RepMaven\org\apache\tomcat\embed\tomcat-embed-jasper\9.0.33\tomcat-embed-jasper-9.0.33.jar
D:\Environment\RepMaven\org\eclipse\jdt\ecj\3.18.0\ecj-3.18.0.jar
D:\Environment\RepMaven\jstl\jstl\1.2\jstl-1.2.jar
D:\Environment\RepMaven\mysql\mysql-connector-java\5.1.47\mysql-connector-java-5.1.47.jar
D:\Environment\RepMaven\tk\mybatis\mapper-spring-boot-starter\2.1.5\mapper-spring-boot-starter-2.1.5.jar
D:\Environment\RepMaven\org\springframework\boot\spring-boot-starter-jdbc\2.2.6.RELEASE\spring-boot-starter-jdbc-2.2.6.RELEASE.jar
D:\Environment\RepMaven\com\zaxxer\HikariCP\3.4.2\HikariCP-3.4.2.jar
D:\Environment\RepMaven\org\springframework\spring-jdbc\5.2.5.RELEASE\spring-jdbc-5.2.5.RELEASE.jar
D:\Environment\RepMaven\org\mybatis\mybatis\3.4.6\mybatis-3.4.6.jar
D:\Environment\RepMaven\org\mybatis\mybatis-spring\1.3.2\mybatis-spring-1.3.2.jar
D:\Environment\RepMaven\tk\mybatis\mapper-core\1.1.5\mapper-core-1.1.5.jar
D:\Environment\RepMaven\javax\persistence\persistence-api\1.0\persistence-api-1.0.jar
D:\Environment\RepMaven\tk\mybatis\mapper-base\1.1.5\mapper-base-1.1.5.jar
D:\Environment\RepMaven\tk\mybatis\mapper-weekend\1.1.5\mapper-weekend-1.1.5.jar
D:\Environment\RepMaven\tk\mybatis\mapper-spring\1.1.5\mapper-spring-1.1.5.jar
D:\Environment\RepMaven\tk\mybatis\mapper-extra\1.1.5\mapper-extra-1.1.5.jar
D:\Environment\RepMaven\tk\mybatis\mapper-spring-boot-autoconfigure\2.1.5\mapper-spring-boot-autoconfigure-2.1.5.jar
D:\Environment\RepMaven\com\alibaba\druid-spring-boot-starter\1.1.10\druid-spring-boot-starter-1.1.10.jar
D:\Environment\RepMaven\com\alibaba\druid\1.1.10\druid-1.1.10.jar
D:\Environment\RepMaven\org\slf4j\slf4j-api\1.7.30\slf4j-api-1.7.30.jar
D:\Environment\RepMaven\org\mybatis\generator\mybatis-generator-core\1.3.3\mybatis-generator-core-1.3.3.jar
D:\Environment\RepMaven\io\springfox\springfox-swagger2\2.7.0\springfox-swagger2-2.7.0.jar
D:\Environment\RepMaven\io\swagger\swagger-annotations\1.5.13\swagger-annotations-1.5.13.jar
D:\Environment\RepMaven\io\swagger\swagger-models\1.5.13\swagger-models-1.5.13.jar
D:\Environment\RepMaven\com\fasterxml\jackson\core\jackson-annotations\2.10.3\jackson-annotations-2.10.3.jar
D:\Environment\RepMaven\io\springfox\springfox-spi\2.7.0\springfox-spi-2.7.0.jar
D:\Environment\RepMaven\io\springfox\springfox-core\2.7.0\springfox-core-2.7.0.jar
D:\Environment\RepMaven\net\bytebuddy\byte-buddy\1.10.8\byte-buddy-1.10.8.jar
D:\Environment\RepMaven\io\springfox\springfox-schema\2.7.0\springfox-schema-2.7.0.jar
D:\Environment\RepMaven\io\springfox\springfox-swagger-common\2.7.0\springfox-swagger-common-2.7.0.jar
D:\Environment\RepMaven\io\springfox\springfox-spring-web\2.7.0\springfox-spring-web-2.7.0.jar
D:\Environment\RepMaven\org\reflections\reflections\0.9.11\reflections-0.9.11.jar
D:\Environment\RepMaven\org\javassist\javassist\3.21.0-GA\javassist-3.21.0-GA.jar
D:\Environment\RepMaven\com\google\guava\guava\18.0\guava-18.0.jar
D:\Environment\RepMaven\com\fasterxml\classmate\1.5.1\classmate-1.5.1.jar
D:\Environment\RepMaven\org\springframework\plugin\spring-plugin-core\1.2.0.RELEASE\spring-plugin-core-1.2.0.RELEASE.jar
D:\Environment\RepMaven\org\springframework\plugin\spring-plugin-metadata\1.2.0.RELEASE\spring-plugin-metadata-1.2.0.RELEASE.jar
D:\Environment\RepMaven\org\mapstruct\mapstruct\1.1.0.Final\mapstruct-1.1.0.Final.jar
D:\Environment\RepMaven\io\springfox\springfox-swagger-ui\2.7.0\springfox-swagger-ui-2.7.0.jar
D:\Environment\RepMaven\org\projectlombok\lombok\1.18.12\lombok-1.18.12.jar
D:\Environment\RepMaven\redis\clients\jedis\3.1.0\jedis-3.1.0.jar
D:\Environment\RepMaven\org\apache\commons\commons-pool2\2.7.0\commons-pool2-2.7.0.jar
D:\Environment\RepMaven\org\springframework\boot\spring-boot-starter-amqp\2.2.6.RELEASE\spring-boot-starter-amqp-2.2.6.RELEASE.jar
D:\Environment\RepMaven\org\springframework\spring-messaging\5.2.5.RELEASE\spring-messaging-5.2.5.RELEASE.jar
D:\Environment\RepMaven\org\springframework\amqp\spring-rabbit\2.2.5.RELEASE\spring-rabbit-2.2.5.RELEASE.jar
D:\Environment\RepMaven\com\rabbitmq\amqp-client\5.7.3\amqp-client-5.7.3.jar
D:\Environment\RepMaven\org\springframework\amqp\spring-amqp\2.2.5.RELEASE\spring-amqp-2.2.5.RELEASE.jar
D:\Environment\RepMaven\org\springframework\retry\spring-retry\1.2.5.RELEASE\spring-retry-1.2.5.RELEASE.jar
D:\Environment\RepMaven\org\springframework\spring-tx\5.2.5.RELEASE\spring-tx-5.2.5.RELEASE.jar
D:\Environment\RepMaven\org\springframework\boot\spring-boot-configuration-processor\2.2.4.RELEASE\spring-boot-configuration-processor-2.2.4.RELEASE.jar
E:\SoftWare\IntelliJ IDEA 2019.1.1\lib\idea_rt.jar
C:\Users\root\.IntelliJIdea2019.1\system\captureAgent\debugger-agent.jar
雙親委派機制
什么是雙親委派機制?
簡單的說:先找父親加載,不行再由兒子自己加載。
通俗的說: 當我們需要加載某個類時會先委托父加載器尋找目標類,找不到再委托上層父加載器加載,如果所有父加載器在自己的加載類路徑下都找不到目標類,則在自己的類加載路徑中查找并載入目標類。
舉例說明:我們有個類A.class ,最先會找應(yīng)用程序類加載器AppClassLoader 加載,AppClassLoader
會先委托擴展類加載器ExtClassLoader加載,擴展類加載器再委托引導(dǎo)類加載器BootClassLoader,頂層引導(dǎo)類加載器BootClassLoader在自己的類加載路徑里
沒找到A類,則向下退回加載A類的請求,擴展類加載器ExtClassLoader收到回復(fù)就自己加載,在自己的類加載路徑里找了半天也沒找到A類,又向下退回A類的加載請求給應(yīng)用程序類加載器AppClassLoader
,應(yīng)用程序類加載器 在自己的類加載路徑里找A類,結(jié)果找到了就自己加載了。
為什么要設(shè)計雙親委派機制?
沙箱安全機制:自己寫的java.lang.String.class類不會被加載,這樣便可以防止核心API庫被隨意篡改 。
避免類的重復(fù)加載:當父親已經(jīng)加載了該類時,就沒有必要子ClassLoader再加載一次,保證被加載類的唯一性。
應(yīng)用程序類加載器AppClassLoader加載類的雙親委派機制源碼
private final ClassLoader parent;
protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException{
synchronized (getClassLoadingLock(name)) {
// 首先,檢查請求的類是否已經(jīng)被加載過
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
//父加載器不為空,調(diào)用父加載器loadClass()方法處理
c = parent.loadClass(name, false);
} else {
//父加載器為空,使用啟動類加載器 BootstrapClassLoader 加載
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
//拋出異常說明父類加載器無法完成加載請求
}
if (c == null) {
long t1 = System.nanoTime();
//自己嘗試加載
c = findClass(name);
// 這是定義類裝入器;記錄統(tǒng)計信息
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
執(zhí)行步驟:
首先,檢查一下指定名稱的類是否已經(jīng)加載過,如果加載過了,就不需要再加載,直接返回。
如果此類沒有加載過,那么,再判斷一下是否有父加載器;如果有父加載器,則由父加載器加載(即調(diào)用parent.loadClass(name, false))或者是調(diào)用bootstrap類加載器來加載。
如果父加載器及bootstrap類加載器都沒有找到指定的類,那么調(diào)用當前類加載器的findClass方法來完成類加載。
String類加載示例:
package java.lang;
/**
* Description: String used for 我自定義的String類
* Created by root on 2020/8/11 10:33
*/
public class String {
public static void main(String[] args) {
System.out.println("我自定義的String類");
}
}
執(zhí)行結(jié)果:
錯誤: 在類 java.lang.String 中找不到 main 方法, 請將 main 方法定義為:
public static void main(String[] args)
否則 JavaFX 應(yīng)用程序類必須擴展javafx.application.Application
分析:首先由于全限定類名java.lang.String等于jdk中的String類,當AppClassLoader加載該String時,判斷java.lang.String已經(jīng)加載,便不會再次加載。所以執(zhí)行的依舊是jdk中的String,但是系統(tǒng)的java.lang.String中沒有main()方法,所以會報錯。這是一種安全機制。
JVM類加載機制
全盤負責委托機制,當一個類加載器負責加載某個Class時,該Class所依賴的和引用的其他Class也將由該類加載器負責載入,除非顯示使用另外一個類加載器來載入
雙親委派機制,先讓父類加載器試圖加載該類,只有在父類加載器無法加載該類時才嘗試從自己的類路徑中加載該類
緩存機制,緩存機制將會保證所有加載過的Class都會被緩存,當程序中需要使用某個Class時,類加載器先從緩存區(qū)尋找該Class,只有緩存區(qū)不存在,系統(tǒng)才會讀取該類對應(yīng)的二進制數(shù)據(jù),并將其轉(zhuǎn)換成Class對象,存入緩存區(qū)。這就是為什么修改了Class后,必須重啟JVM,程序的修改才會生效
自定義類加載器示例:
自定義類加載器只需要繼承 java.lang.ClassLoader
類,該類有兩個核心方法,一個是loadClass(String, boolean),實現(xiàn)了 雙親委派機制
,還有一個方法是findClass,默認實現(xiàn)是空 方法,所以我們自定義類加載器主要是 重寫 findClass 方法 。