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

歡迎訪問 生活随笔!

生活随笔

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

java

Java类加载的那些事

發(fā)布時(shí)間:2023/12/3 java 49 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java类加载的那些事 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

轉(zhuǎn)載自?Java類加載的那些事

?

前言

Java源代碼被編譯成class字節(jié)碼,最終需要加載到虛擬機(jī)中才能運(yùn)行。整個(gè)生命周期包括:加載、驗(yàn)證、準(zhǔn)備、解析、初始化、使用和卸載7個(gè)階段。?

加載

1、通過一個(gè)類的全限定名獲取描述此類的二進(jìn)制字節(jié)流;?

2、將這個(gè)字節(jié)流所代表的靜態(tài)存儲(chǔ)結(jié)構(gòu)保存為方法區(qū)的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu);?

3、在java堆中生成一個(gè)代表這個(gè)類的java.lang.Class對(duì)象,作為訪問方法區(qū)的入口;

虛擬機(jī)設(shè)計(jì)團(tuán)隊(duì)把加載動(dòng)作放到JVM外部實(shí)現(xiàn),以便讓應(yīng)用程序決定如何獲取所需的類,實(shí)現(xiàn)這個(gè)動(dòng)作的代碼稱為“類加載器”,JVM提供了3種類加載器:?

1、啟動(dòng)類加載器(Bootstrap ClassLoader):負(fù)責(zé)加載 JAVAHOME\lib 目錄中的,或通過-Xbootclasspath參數(shù)指定路徑中的,且被虛擬機(jī)認(rèn)可(按文件名識(shí)別,如rt.jar)的類。?

2、擴(kuò)展類加載器(Extension ClassLoader):負(fù)責(zé)加載 JAVAHOME\lib\ext 目錄中的,或通過java.ext.dirs系統(tǒng)變量指定路徑中的類庫(kù)。?

3、應(yīng)用程序類加載器(Application ClassLoader):負(fù)責(zé)加載用戶路徑(classpath)上的類庫(kù)。

JVM基于上述類加載器,通過雙親委派模型進(jìn)行類的加載,當(dāng)然我們也可以通過繼承java.lang.ClassLoader實(shí)現(xiàn)自定義的類加載器。

?

雙親委派模型工作過程:當(dāng)一個(gè)類加載器收到類加載任務(wù),優(yōu)先交給其父類加載器去完成,因此最終加載任務(wù)都會(huì)傳遞到頂層的啟動(dòng)類加載器,只有當(dāng)父類加載器無法完成加載任務(wù)時(shí),才會(huì)嘗試執(zhí)行加載任務(wù)。

雙親委派模型有什么好處? 比如位于rt.jar包中的類java.lang.Object,無論哪個(gè)加載器加載這個(gè)類,最終都是委托給頂層的啟動(dòng)類加載器進(jìn)行加載,確保了Object類在各種加載器環(huán)境中都是同一個(gè)類。

驗(yàn)證

為了確保Class文件符合當(dāng)前虛擬機(jī)要求,需要對(duì)其字節(jié)流數(shù)據(jù)進(jìn)行驗(yàn)證,主要包括格式驗(yàn)證、元數(shù)據(jù)驗(yàn)證、字節(jié)碼驗(yàn)證和符號(hào)引用驗(yàn)證。?

  • 格式驗(yàn)證:驗(yàn)證字節(jié)流是否符合class文件格式的規(guī)范,并且能被當(dāng)前虛擬機(jī)處理,如是否以魔數(shù)0xCAFEBABE開頭、主次版本號(hào)是否在當(dāng)前虛擬機(jī)處理范圍內(nèi)、常量池是否有不支持的常量類型等。只有經(jīng)過格式驗(yàn)證的字節(jié)流,才會(huì)存儲(chǔ)到方法區(qū)的數(shù)據(jù)結(jié)構(gòu),剩余3個(gè)驗(yàn)證都基于方法區(qū)的數(shù)據(jù)進(jìn)行。

  • 元數(shù)據(jù)驗(yàn)證:對(duì)字節(jié)碼描述的數(shù)據(jù)進(jìn)行語義分析,以保證符合Java語言規(guī)范,如是否繼承了final修飾的類、是否實(shí)現(xiàn)了父類的抽象方法、是否覆蓋了父類的final方法或final字段等。

  • 字節(jié)碼驗(yàn)證:對(duì)類的方法體進(jìn)行分析,確保在方法運(yùn)行時(shí)不會(huì)有危害虛擬機(jī)的事件發(fā)生,如保證操作數(shù)棧的數(shù)據(jù)類型和指令代碼序列的匹配、保證跳轉(zhuǎn)指令的正確性、保證類型轉(zhuǎn)換的有效性等。

  • 符號(hào)引用驗(yàn)證:為了確保后續(xù)的解析動(dòng)作能夠正常執(zhí)行,對(duì)符號(hào)引用進(jìn)行驗(yàn)證,如通過字符串描述的全限定名是都能找到對(duì)應(yīng)的類、在指定類中是否存在符合方法的字段描述符等。

  • 準(zhǔn)備

    在準(zhǔn)備階段,為類變量(static修飾)在方法區(qū)中分配內(nèi)存并設(shè)置初始值。

    private static int var = 100;

    準(zhǔn)備階段完成后,var 值為0,而不是100。在初始化階段,才會(huì)把100賦值給val,但是有個(gè)特殊情況:

    private static final int VAL= 100;

    在編譯階段會(huì)為VAL生成ConstantValue屬性,在準(zhǔn)備階段虛擬機(jī)會(huì)根據(jù)ConstantValue屬性將VAL賦值為100。

    解析

    解析階段是將常量池中的符號(hào)引用替換為直接引用的過程,符號(hào)引用和直接引用有什么不同??

    1、符號(hào)引用使用一組符號(hào)來描述所引用的目標(biāo),可以是任何形式的字面常量,定義在Class文件格式中。?

    2、直接引用可以是直接指向目標(biāo)的指針、相對(duì)偏移量或則能間接定位到目標(biāo)的句柄。

    初始化

    初始化階段是執(zhí)行類構(gòu)造器方法的過程,方法由類變量的賦值動(dòng)作和靜態(tài)語句塊按照在源文件出現(xiàn)的順序合并而成,該合并操作由編譯器完成。

    private static int value = 100;static int a = 100;static int b = 100;static int c;static {c = a + b;System.out.println("it only run once");}

    1、方法對(duì)于類或接口不是必須的,如果一個(gè)類中沒有靜態(tài)代碼塊,也沒有靜態(tài)變量的賦值操作,那么編譯器不會(huì)生成;?

    2、方法與實(shí)例構(gòu)造器不同,不需要顯式的調(diào)用父類的方法,虛擬機(jī)會(huì)保證父類的優(yōu)先執(zhí)行;?

    3、為了防止多次執(zhí)行,虛擬機(jī)會(huì)確保方法在多線程環(huán)境下被正確的加鎖同步執(zhí)行,如果有多個(gè)線程同時(shí)初始化一個(gè)類,那么只有一個(gè)線程能夠執(zhí)行方法,其它線程進(jìn)行阻塞等待,直到執(zhí)行完成。?

    4、注意:執(zhí)行接口的方法不需要先執(zhí)行父接口的,只有使用父接口中定義的變量時(shí),才會(huì)執(zhí)行。

    類初始化場(chǎng)景

    虛擬機(jī)中嚴(yán)格規(guī)定了有且只有5種情況必須對(duì)類進(jìn)行初始化。?

    1、執(zhí)行new、getstatic、putstatic和invokestatic指令;?

    2、使用reflect對(duì)類進(jìn)行反射調(diào)用;?

    3、初始化一個(gè)類的時(shí)候,父類還沒有初始化,會(huì)事先初始化父類;?

    4、啟動(dòng)虛擬機(jī)時(shí),需要初始化包含main方法的類;?

    5、在JDK1.7中,如果java.lang.invoke.MethodHandler實(shí)例最后的解析結(jié)果REFgetStatic、REFputStatic、REF_invokeStatic的方法句柄,并且這個(gè)方法句柄對(duì)應(yīng)的類沒有進(jìn)行初始化;

    以下幾種情況,不會(huì)觸發(fā)類初始化?

    1、通過子類引用父類的靜態(tài)字段,只會(huì)觸發(fā)父類的初始化,而不會(huì)觸發(fā)子類的初始化。

    class Parent {static int a = 100;static {System.out.println("parent init!");} }class Child extends Parent {static {System.out.println("child init!");} }public class Init{ public static void main(String[] args){ System.out.println(Child.a); } }

    輸出結(jié)果為: parent init! 100

    2、定義對(duì)象數(shù)組,不會(huì)觸發(fā)該類的初始化。

    public class Init{ public static void main(String[] args){ Parent[] parents = new Parent[10];} }

    無輸出,說明沒有觸發(fā)類Parent的初始化,但是這段代碼做了什么?先看看生成的字節(jié)碼指令

    ?anewarray指令為新數(shù)組分配空間,并觸發(fā)[Lcom.ctrip.ttd.whywhy.Parent類的初始化,這個(gè)類由虛擬機(jī)自動(dòng)生成。

    3、常量在編譯期間會(huì)存入調(diào)用類的常量池中,本質(zhì)上并沒有直接引用定義常量的類,不會(huì)觸發(fā)定義常量所在的類。

    class Const {static final int A = 100;static {System.out.println("Const init");} }public class Init{ public static void main(String[] args){ System.out.println(Const.A); } }

    輸出: 100?
    說明沒有觸發(fā)類Const的初始化,在編譯階段,Const類中常量A的值100存儲(chǔ)到Init類的常量池中,這兩個(gè)類在編譯成class文件之后就沒有聯(lián)系了。

    4、通過類名獲取Class對(duì)象,不會(huì)觸發(fā)類的初始化。

    public class test {public static void main(String[] args) throws ClassNotFoundException {Class c_dog = Dog.class;Class clazz = Class.forName("zzzzzz.Cat");} }class Cat {private String name;private int age;static {System.out.println("Cat is load");} }class Dog {private String name;private int age;static {System.out.println("Dog is load");}

    執(zhí)行結(jié)果:Cat is load

    所以通過Dog.class并不會(huì)觸發(fā)Dog類的初始化動(dòng)作。

    5、通過Class.forName加載指定類時(shí),如果指定參數(shù)initialize為false時(shí),也不會(huì)觸發(fā)類初始化,其實(shí)這個(gè)參數(shù)是告訴虛擬機(jī),是否要對(duì)類進(jìn)行初始化。

    public class test {public static void main(String[] args) throws ClassNotFoundException {Class clazz = Class.forName("zzzzzz.Cat", false, Cat.class.getClassLoader());} } class Cat {private String name;private int age;static {System.out.println("Cat is load");} }

    6、通過ClassLoader默認(rèn)的loadClass方法,也不會(huì)觸發(fā)初始化動(dòng)作

    new ClassLoader(){}.loadClass("zzzzzz.Cat");

    ?

    ?

    ?

    總結(jié)

    以上是生活随笔為你收集整理的Java类加载的那些事的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

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