日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > java >内容正文

java

Java虚拟机:对象创建过程与类加载机制、双亲委派模型

發(fā)布時(shí)間:2024/9/30 java 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java虚拟机:对象创建过程与类加载机制、双亲委派模型 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

一、對象的創(chuàng)建過程:

1、對象的創(chuàng)建過程:

對象的創(chuàng)建過程一般是從 new 指令(JVM層面)開始的,整個(gè)創(chuàng)建過程如下:

(1)首先檢查 new 指令的參數(shù)是否能在常量池中定位到一個(gè)類的符號引用;

(2)如果沒有,說明類還沒有被加載,則須先執(zhí)行相應(yīng)的類加載、解析和初始化等類加載過程,該過程的具體步驟詳見下文

(3)如果有,虛擬機(jī)將在堆中為新生對象分配內(nèi)存。分配內(nèi)存方式有:指針碰撞和空閑列表,具體選擇哪種分配方式由 Java 堆是否規(guī)整決定,而?Java 堆是否規(guī)整又由所采用的垃圾收集器是否帶有壓縮整理功能決定。

  • 指針碰撞:如果Java堆是絕對規(guī)整的,所有用過的內(nèi)存都放在一邊,所有沒用過的內(nèi)存存放在另一邊,中間存放一個(gè)指針作為分界點(diǎn)指示器。分配內(nèi)存時(shí),將指針從用過的內(nèi)存區(qū)域向空閑內(nèi)存區(qū)域移動(dòng)等距離區(qū)域。
  • 空閑列表:如果Java不是規(guī)整的,這時(shí),虛擬機(jī)就必須維護(hù)一張列表,列表上記錄了可用的內(nèi)存塊,在分配內(nèi)存時(shí),從列表上找到一個(gè)足夠大的連續(xù)內(nèi)存塊分配給對象,并更新列表上的記錄。

? ? ? ? 在分配對象內(nèi)存空間的過程中,需要考慮在并發(fā)情況下,線程是否安全的問題。因?yàn)閯?chuàng)建對象在虛擬機(jī)中是非常頻繁的行為,可能出現(xiàn)正在給對象A分配內(nèi)存,指針還沒來得及修改,對象B又同時(shí)使用了原來的指針來分配內(nèi)存的情況。因此必須要保證線程安全,解決這個(gè)問題有兩種方案:

  • CAS以及失敗重試(比較和交換機(jī)制):對分配內(nèi)存空間的操作進(jìn)行同步處理,實(shí)際上虛擬機(jī)采用CAS配上失敗重試的方式保證更新操作的原子性。CAS操作需要輸入兩個(gè)數(shù)值,一個(gè)舊值(操作前期望的值)和一個(gè)新值,在操作期間先比較舊值有沒有發(fā)送變化,如果沒有變化,才交換成新值,否則不進(jìn)行交換。
  • TLAB(Thread Local Allocation Buffer,本地線程分配緩存):把內(nèi)存分配的動(dòng)作按照線程劃分在不同的空間之中進(jìn)行,即每個(gè)線程在Java堆中預(yù)先分配一小塊私有內(nèi)存,也就是本地線程分配緩沖。TLAB的目的是在為新對象分配內(nèi)存空間時(shí),讓每個(gè)Java應(yīng)用線程能在使用自己專屬的分配指針來分配空間,減少同步開銷。

(4)內(nèi)存分配完成后,虛擬機(jī)需要將分配到的內(nèi)存空間都初始化為零值,保證了對象實(shí)例的字段在 Java 代碼中可以不賦初始值就可以直接使用;

(5)對對象進(jìn)行必要的設(shè)置,例如是哪個(gè)對象的實(shí)例、如何才能找到類元信息、對象的哈希碼、GC 分代年齡等信息,這些信息存放在對象頭中。

(6)執(zhí)行 init 方法,把對象按照程序員意愿進(jìn)行初始化。

至此,一個(gè)對象就被創(chuàng)建完畢,同時(shí)會在Java棧中分配一個(gè)引用指向這個(gè)對象,通過棧上面的引用可以訪問堆中的具體對象,訪問對象主要有兩種方式:通過句柄訪問對象和直接指針訪問對象。

2、對象的訪問方式:

(1)通過句柄訪問對象:

????????在Java堆中劃出一塊內(nèi)存專門作為句柄池,reference中存儲的就是對象的句柄地址,而句柄中包含了對象實(shí)例數(shù)據(jù)與類型數(shù)據(jù)各自的地址地址信息。

(2)通過直接指針訪問對象:

(3)優(yōu)劣對比:

? ?① 使用句柄,reference中存儲的是穩(wěn)定的句柄地址,在對象被移動(dòng)時(shí)只會改變句柄中的實(shí)例數(shù)據(jù)指針,而reference本身不需要修改。

? ?② 直接指針,速度快,節(jié)省一次指定定位的開銷。

二、類加載機(jī)制:

????????Java 文件中的代碼在編譯后,就會生成JVM能夠識別的二進(jìn)制字節(jié)流 class 文件,class 文件中描述的各種信息,都需要加載到虛擬機(jī)中才能被運(yùn)行和使用。

????????類加載機(jī)制,就是虛擬機(jī)把類的數(shù)據(jù)從class文件加載到內(nèi)存,并對數(shù)據(jù)進(jìn)行校檢,轉(zhuǎn)換解析和初始化,最終形成可以被虛擬機(jī)直接使用的Java類型的過程。類從加載到虛擬機(jī)內(nèi)存開始,到卸載出內(nèi)存結(jié)束,整個(gè)生命周期包括七個(gè)階段,如下圖(每個(gè)過程作用見文章第四部分):

2.1、加載階段:

這階段的虛擬機(jī)需要完成三件事情:

  • (1)通過一個(gè)類的全限定名來獲取定義此類的二進(jìn)制字節(jié)流。
  • (2)將這個(gè)字節(jié)流所代表的靜態(tài)存儲結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu)。
  • (3)在內(nèi)存中生成一個(gè)代表這個(gè)類的java.lang.Class對象,作為方法區(qū)這個(gè)類的各種數(shù)據(jù)的訪問入口。

2.2、驗(yàn)證階段:

????????這階段是為了確保class文件的字節(jié)流包含的信息符合當(dāng)前虛擬機(jī)的要求,不會危害虛擬機(jī)自身的安全。分為4個(gè)校檢動(dòng)作:

  • (1)文件格式驗(yàn)證:驗(yàn)證字節(jié)流是否符合class文件格式的規(guī)范,并且能被當(dāng)前版本的虛擬機(jī)處理,通過該階段后,字節(jié)流會進(jìn)入內(nèi)存的方法區(qū)中進(jìn)行儲存。
  • (2)元數(shù)據(jù)驗(yàn)證:對字節(jié)碼描述的信息進(jìn)行語言分析,對類的元數(shù)據(jù)信息進(jìn)行語義校驗(yàn),確保其描述的信息符合java語言規(guī)范要求。
  • (3)字節(jié)碼驗(yàn)證:通過數(shù)據(jù)流和控制流分析,確定程序語義是合法的、符合邏輯的。這個(gè)階段對類的方法進(jìn)行校驗(yàn)分析,保證類的方法在運(yùn)行時(shí)不會做出危害虛擬機(jī)安全的事件。
  • (4)符號引用驗(yàn)證:對類自身以外的信息(常量池中各種符號引用)的信息進(jìn)行校檢,確保解析動(dòng)作能正常執(zhí)行(該動(dòng)作發(fā)生在解析階段中)

2.3、準(zhǔn)備階段:

????????正式為類變量分配內(nèi)存空間并設(shè)置數(shù)據(jù)類型零值。這個(gè)階段進(jìn)行內(nèi)存分配的僅包括類變量(static修飾),不包括實(shí)例變量,實(shí)例變量會在對象實(shí)例化時(shí)隨對象一起分配在java堆。

public static int value= 123 ; //變量value在準(zhǔn)備階段過后的初始值是0,不是123. public static final int value = 123 ; //特殊情況:會生成ConstantValue屬性,在準(zhǔn)備階段初始值是123.

final、static、static?final修飾的字段賦值的區(qū)別:

(1)static修飾的字段在準(zhǔn)備階段被初始化為0或null等默認(rèn)值,然后在初始化階段(觸發(fā)類構(gòu)造器)才會被賦予代碼中設(shè)定的值,如果沒有設(shè)定值,那么它的值就為默認(rèn)值。

(2)static final修飾的字段在Javac時(shí)生成ConstantValue屬性,在類加載的準(zhǔn)備階段根據(jù)ConstantValue的值為該字段賦值,它沒有默認(rèn)值,必須顯式地賦值,否則Javac時(shí)會報(bào)錯(cuò)。可以理解為在編譯期即把結(jié)果放入了常量池中。

(3)final修飾的字段在運(yùn)行時(shí)被初始化(可以直接賦值,也可以在實(shí)例構(gòu)造器中()賦值),一旦賦值便不可更改。

2.4、解析階段:

????????將常量池的符號引用替換為直接引用的過程。解析動(dòng)作主要針對類或接口、字段、類方法、接口方法、方法類型、方法句柄、和調(diào)用限定符7類符號引用。

????????對同一符號引用進(jìn)行多次解析請求是很常見的事情,除invokedynamic指令以外,虛擬機(jī)可以對第一次解析的結(jié)果進(jìn)行緩存,從而避免解析動(dòng)作重復(fù)進(jìn)行。invokedynamic對應(yīng)的引用稱為“動(dòng)態(tài)調(diào)用限定符”,必須等到程序?qū)嶋H運(yùn)行到這條指定的時(shí)候,解析動(dòng)作才能進(jìn)行。因此,當(dāng)碰到由前面的invokedynamic指令觸發(fā)過的解析的符號引用時(shí),并不意味著這個(gè)解析結(jié)果對其他的invokedynamic指令也同樣生效。

? (1)符號引用:符號引用是以一組符號來描述所引用的目標(biāo),符號可以是任何字面量,只要使用時(shí)無歧義定位到目標(biāo)即可。符號引用與虛擬機(jī)的內(nèi)存布局無關(guān),引用的目標(biāo)并不一定已經(jīng)加載到內(nèi)存中。各種虛擬機(jī)實(shí)現(xiàn)的內(nèi)存布局可以不相同,但是他們能接受的符號引用必須都是一致的,符號引用的字面量形式明確地定義在java虛擬機(jī)規(guī)范的calss文件格式中。

? (2)直接引用:直接引用是可以直接定位到目標(biāo)的指針、相對偏移量或是一個(gè)能間接定位目標(biāo)的句柄。直接引用是與虛擬機(jī)的內(nèi)存布局相關(guān)的,同一個(gè)符號引用在不同虛擬機(jī)實(shí)例上翻譯出來的直接引用一般不會相同。如果有了直接引用,那引用的目標(biāo)必定已經(jīng)在內(nèi)存中存在。

2.5、初始化:

????????初始化階段才真正執(zhí)行類中定義的java代碼。執(zhí)行類構(gòu)造器()方法的過程,并按程序的設(shè)置去初始類變量和其他資源。

2.5.1、類的主動(dòng)引用:

在初始化階段,有且只有5種場景必須立即對類進(jìn)行“初始化”,稱為主動(dòng)引用:

? ? (1)遇到new、getstatic、putstatic、invokestatic這4條指定時(shí)。對應(yīng)的場景是:使用new關(guān)鍵字實(shí)例化對象的時(shí)候,讀取或設(shè)置一個(gè)類的靜態(tài)字段(被final修飾、已經(jīng)在編譯期把結(jié)果放入常量池的靜態(tài)字段除外),以及調(diào)用一個(gè)類的靜態(tài)方法的時(shí)候。

? ? (2)使用java.lang.reflect包的方法對類進(jìn)行發(fā)射調(diào)用的時(shí)候。

? ? (3)當(dāng)初始化一個(gè)類的時(shí)候,如果發(fā)現(xiàn)其父類還沒進(jìn)行初始化,則必須對父類進(jìn)行初始化。(與接口的區(qū)別:接口在初始化時(shí),并不要求其父接口全部都完成了初始化,只有在真正使用到父接口的時(shí)候,才會初始化)

? ? (4)當(dāng)虛擬機(jī)啟動(dòng)時(shí),用戶指定的要執(zhí)行的主類(包含main方法的類)。

????(5)java.lang.invoke.MethodHandle實(shí)例最后的解析結(jié)果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且這個(gè)句柄所對應(yīng)的類沒有進(jìn)行過初始化,則需要觸發(fā)其初始化。

2.5.2、類的被動(dòng)引用:

除了主動(dòng)引用,其他引用類的方式都不會觸發(fā)初始化,稱為被動(dòng)引用:

? ? (1)對于靜態(tài)字段,只有直接定義這個(gè)字段的類才會被初始化,通過其子類來引用父類中定義的靜態(tài)字段,只會觸發(fā)其父類的初始化而不會觸發(fā)子類的初始化。

//父類 public class SuperClass { //靜態(tài)變量value public static int value =123456; //靜態(tài)塊,父類初始化時(shí)會調(diào)用 static{ System.out.println("父類初始化!"); } } //子類 public class SubClass extends SuperClass{ //靜態(tài)塊,子類初始化時(shí)會調(diào)用 static{ System.out.println("子類初始化!"); } } //主類、測試類 public class InitTest { public static void main(String[] args){ System.out.println(SubClass.value); //輸出結(jié)果:父類初始化! 123456} }

? ? (2)通過數(shù)組定義來引用類,不會觸發(fā)此類的初始化。

//父類 public class SuperClass { //靜態(tài)變量value public static int value = 123456; //靜態(tài)塊,父類初始化時(shí)會調(diào)用 static{ System.out.println("父類初始化!"); } } //主類、測試類 public class InitTest { public static void main(String[] args){ SuperClass[] test = new SuperClass[10]; //輸出結(jié)果:沒有任何輸出結(jié)果} }

? ? (3)常量在編譯階段會存入調(diào)用類的常量池中,本質(zhì)上并沒有直接引用到定義常量的類,因此不會觸發(fā)定義常量的類的初始化。

//定義常量的類 public class ConstClass { static{ System.out.println("常量類初始化!"); } public static final String HELLOWORLD = "hello world!"; } //主類、測試類 public class InitTest { public static void main(String[] args){ System.out.println(ConstClass.HELLOWORLD); //輸出結(jié)果:hello world} }

2.5.3、()方法的特點(diǎn):?

???(1)()方法是由編譯器自動(dòng)收集類中的所有類變量的賦值動(dòng)作和靜態(tài)語句塊中的語句合并產(chǎn)生的,收集的順序是由語句在源文件中出現(xiàn)的順序決定的。靜態(tài)語句塊中只能訪問到定義在靜態(tài)語句塊之前的變量,定義在它之后的變量,在前面的靜態(tài)語句塊中可以賦值,但是不可以訪問。

public class Test {static{i=0; //給變量賦值可以正常編譯通過System.out.println(i); //編譯器會提示非法向前引用}static int i=1; }

? ? (2)()方法與實(shí)例構(gòu)造器()方法不同,它不需要顯示調(diào)用父類構(gòu)造器,虛擬機(jī)會保證子類的()方法執(zhí)行之前,父類的()方法已經(jīng)執(zhí)行完畢,所以父類中定義的靜態(tài)語句塊要優(yōu)先于子類的變量賦值操作,虛擬機(jī)中第一個(gè)被執(zhí)行的()方法的類是java.lang.Object。

? ? (3)()方法對于類或接口并不是必需的,如果一個(gè)類中沒有靜態(tài)語句塊,也就沒有對變量的賦值操作,那么編譯器可以不為這個(gè)類生成()方法。

? ? (4)接口中不能使用靜態(tài)語句塊,仍然有變量初始化操作,因此仍然會生成()方法,與類不同的是,執(zhí)行接口中的()方法不需要先執(zhí)行父接口的()方法。只有父接口中定義的變量被使用是,才需要初始化父接口,同時(shí),接口實(shí)現(xiàn)類在初始化時(shí)也不會執(zhí)行接口的()方法。

? ? (5)()方法在多線程環(huán)境中被正確的加鎖、同步,多個(gè)線程同時(shí)去初始化一個(gè)類,只會有一個(gè)線程執(zhí)行()方法,其他線程則需要阻塞等待,直到活動(dòng)線程執(zhí)行()方法完畢,活動(dòng)線程執(zhí)行完畢后,其他線程喚醒后被不會再次進(jìn)入()方法,因?yàn)橥粋€(gè)類加載器下,一個(gè)類型只會被初始化一次。

三、類加載器與雙親委派模型:

3.1、JVM 的類加載器:

? ? ? ? 類加載機(jī)制生命周期的第一階段,即加載階段需要由類加載器來完成的,類加載器根據(jù)一個(gè)類的全限定名讀取類的二進(jìn)制字節(jié)流到JVM中,然后生成對應(yīng)的java.lang.Class對象實(shí)例,

????????在虛擬機(jī)默認(rèn)提供了3種類加載器,啟動(dòng)類加載器(Bootstrap ClassLoader)、擴(kuò)展類加載器(Extension ClassLoader)、應(yīng)用類加載器(Application ClassLoader),如果有必要還可以加入自己定義的類加載器。

(1)啟動(dòng)類加載器(Bootstrap ClassLoader):負(fù)責(zé)加載 在\lib目錄 和 被-Xbootclasspath參數(shù)所指定的路徑中的類庫?

(2)擴(kuò)展類加載器(Extension ClassLoader):負(fù)責(zé)加載 \lib\ext目錄 和 被java.ext.dirs系統(tǒng)變量所指定的路徑中的所有類庫

(3)應(yīng)用程序類加載器(Application ClassLoader):負(fù)責(zé)加載用戶類路徑classPath所指定的類庫,如果應(yīng)用程序中沒有自定義過自己的類加載器,一般情況下這個(gè)就是程序中默認(rèn)的類加載器。

(4)自定義加載器(CustomClassLoader):由應(yīng)用程序根據(jù)自身需要自定義,如 Tomcat、Jboss 都會根據(jù) j2ee 規(guī)范自行實(shí)現(xiàn)。

????????任意一個(gè)類在 JVM 中的唯一性,是由加載它的類加載器和類的全限定名一共同確定的。因此,比較兩個(gè)類是否“相等”的前提是這兩個(gè)類是由同一個(gè)類加載器加載的,否則,即使兩個(gè)類來源于同一個(gè) Class 文件,被同一個(gè)虛擬機(jī)加載,只要加載他們的類加載器不同,那這兩個(gè)類就必定不相等。JVM 的類加載機(jī)制,規(guī)定一個(gè)類有且只有一個(gè)類加載器對它進(jìn)行加載。而如何保證這個(gè)只有一個(gè)類加載器對它進(jìn)行加載呢?則是由雙親委派模型來實(shí)現(xiàn)的。

3.2、雙親委派模型:

????????雙親委派模型要求除了頂層的啟動(dòng)類加載器外,其余類加載器都應(yīng)該有自己的父類加載器。(類加載器之間的父子關(guān)系不是以繼承的關(guān)系實(shí)現(xiàn),而是使用組合關(guān)系來復(fù)用父加載器的代碼)

3.2.1、雙親委派模型的工作原理:

????????如果一個(gè)類加載器收到了類加載的請求,他首先不會自己去嘗試加載這個(gè)類,而是把這個(gè)請求委派給父類加載器去完成,每一個(gè)層級的類加載器都是如此,因此所有請求最終都會被傳到最頂層的啟動(dòng)類加載器中,只有當(dāng)父類加載器反饋?zhàn)约簾o法完成這個(gè)加載請求時(shí),子加載器才會嘗試自己去加載。因此,加載過程可以看成自底向上檢查類是否已經(jīng)加載,然后自頂向下加載類。

3.2.2、雙親委派模型的優(yōu)點(diǎn):

(1)使用雙親委派模型來組織類加載器之間的關(guān)系,Java類隨著它的類加載器一起具備了一種帶有優(yōu)先級的層次關(guān)系。

(2)避免類的重復(fù)加載,當(dāng)父類加載器已經(jīng)加載了該類時(shí),子類加載器就沒必要再加載一次。

(3)解決各個(gè)類加載器的基礎(chǔ)類的統(tǒng)一問題,越基礎(chǔ)的類由越上層的加載器進(jìn)行加載。避免Java核心API中的類被隨意替換,規(guī)避風(fēng)險(xiǎn),防止核心API庫被隨意篡改。

例如類 java.lang.Object,它存在在 rt.jar 中,無論哪一個(gè)類加載器要加載這個(gè)類,最終都是委派給處于模型最頂端的 Bootstrap ClassLoader 進(jìn)行加載,因此 Object 類在程序的各種類加載器環(huán)境中都是同一個(gè)類。相反,如果沒有雙親委派模型而是由各個(gè)類加載器自行加載的話,如果用戶編寫了一個(gè) java.lang.Object 的同名類并放在 ClassPath 中,那系統(tǒng)中將會出現(xiàn)多個(gè)不同的 Object 類,程序?qū)⒒靵y。因此,如果開發(fā)者嘗試編寫一個(gè)與 rt.jar 類庫中重名的 Java 類,可以正常編譯,但是永遠(yuǎn)無法被加載運(yùn)行。

3.3、類加載器源碼:loadClass()

實(shí)現(xiàn)雙親委派的代碼都集中在 java.lang.ClassLoader 的 loadClass() 方法之中,下面我們簡單看下?loadClass() 的源碼是怎么樣的:

public abstract class ClassLoader {// 每個(gè)類加載器都有一個(gè)父加載器private final ClassLoader parent;public Class<?> loadClass(String name) throws ClassNotFoundException {return loadClass(name, false);}protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException{// 首先,檢查該類是否已經(jīng)加載過了Class<?> c = findLoadedClass(name);// 如果沒有加載過if (c == null) {if (parent != null) {// 先委托給父加載器去加載,注意這是個(gè)遞歸調(diào)用c = parent.loadClass(name, false);} else {// 如果父加載器為空,查找 Bootstrap 加載器是不是加載過了c = findBootstrapClassOrNull(name);}// 如果父加載器沒加載成功,調(diào)用自己的 findClass 去加載if (c == null) { c = findClass(name);}} return c;}}// ClassLoader 中 findClass 方式需要被子類覆蓋,下面這段代碼就是對應(yīng)代碼protected Class<?> findClass(String name){//1. 根據(jù)傳入的類名 name,到在特定目錄下去尋找類文件,把 .class 文件讀入內(nèi)存...//2. 調(diào)用 defineClass 將字節(jié)數(shù)組轉(zhuǎn)成 Class 對象return defineClass(buf, off, len);}// 將字節(jié)碼數(shù)組解析成一個(gè) Class 對象,用 native 方法實(shí)現(xiàn)protected final Class<?> defineClass(byte[] b, int off, int len){} }

3.4、如何破壞雙親委派模型:

????????雙親委派過程是在 loadClass() 方法中實(shí)現(xiàn)的,如果想要破壞這種機(jī)制,那么就自定義一個(gè)類加載器(繼承自 ClassLoader),重寫其中的 loadClass() 方法,使其不進(jìn)行雙親委派即可。ClassLoader 中和類加載有關(guān)的方法有很多,除了前面提到了 loadClass(),除此之外,還有 findClass() 和 defineClass() 等,那么這幾個(gè)方法有什么區(qū)別呢:

  • loadClass() 就是主要進(jìn)行類加載的方法,默認(rèn)的雙親委派機(jī)制就實(shí)現(xiàn)在這個(gè)方法中
  • findClass() 根據(jù)名稱或位置加載 .class 字節(jié)碼
  • definclass() 把字節(jié)碼轉(zhuǎn)化為 Class 對象

????????findClass() 是 JDK1.2 之后 ClassLoader 新添加的方法,在 JDK1.2 之后已不提倡用戶直接覆蓋 loadClass() 方法,而是建議把自己的類加載邏輯實(shí)現(xiàn)到 findClass() 方法中,因?yàn)樵?loadClass() 方法的邏輯里,如果父類加載器加載失敗,則會調(diào)用自己的 findClass() 方法來完成加載。而同樣如果你想定義一個(gè)自己的類加載器,并且要遵守雙親委派模型,那么可以繼承 ClassLoader,并且在 findClass() 中實(shí)現(xiàn)你自己的加載邏輯即可

? ? ? ? 對于雙親委派模型的破壞,最經(jīng)典例子就是?Tomcat 容器的類加載機(jī)制了,它實(shí)現(xiàn)了自己的類加載器 WebApp ClassLoader,并且打破了雙親委派模型,在每個(gè)應(yīng)用在部署后,都會創(chuàng)建一個(gè)唯一的類加載器,對此感興趣的讀者可以閱讀后面這篇文章:Tomcat 的類加載機(jī)制

總結(jié)

以上是生活随笔為你收集整理的Java虚拟机:对象创建过程与类加载机制、双亲委派模型的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。