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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

Java类加载的那些事

發布時間:2023/12/3 java 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java类加载的那些事 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

轉載自?Java類加載的那些事

?

前言

Java源代碼被編譯成class字節碼,最終需要加載到虛擬機中才能運行。整個生命周期包括:加載、驗證、準備、解析、初始化、使用和卸載7個階段。?

加載

1、通過一個類的全限定名獲取描述此類的二進制字節流;?

2、將這個字節流所代表的靜態存儲結構保存為方法區的運行時數據結構;?

3、在java堆中生成一個代表這個類的java.lang.Class對象,作為訪問方法區的入口;

虛擬機設計團隊把加載動作放到JVM外部實現,以便讓應用程序決定如何獲取所需的類,實現這個動作的代碼稱為“類加載器”,JVM提供了3種類加載器:?

1、啟動類加載器(Bootstrap ClassLoader):負責加載 JAVAHOME\lib 目錄中的,或通過-Xbootclasspath參數指定路徑中的,且被虛擬機認可(按文件名識別,如rt.jar)的類。?

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

3、應用程序類加載器(Application ClassLoader):負責加載用戶路徑(classpath)上的類庫。

JVM基于上述類加載器,通過雙親委派模型進行類的加載,當然我們也可以通過繼承java.lang.ClassLoader實現自定義的類加載器。

?

雙親委派模型工作過程:當一個類加載器收到類加載任務,優先交給其父類加載器去完成,因此最終加載任務都會傳遞到頂層的啟動類加載器,只有當父類加載器無法完成加載任務時,才會嘗試執行加載任務。

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

驗證

為了確保Class文件符合當前虛擬機要求,需要對其字節流數據進行驗證,主要包括格式驗證、元數據驗證、字節碼驗證和符號引用驗證。?

  • 格式驗證:驗證字節流是否符合class文件格式的規范,并且能被當前虛擬機處理,如是否以魔數0xCAFEBABE開頭、主次版本號是否在當前虛擬機處理范圍內、常量池是否有不支持的常量類型等。只有經過格式驗證的字節流,才會存儲到方法區的數據結構,剩余3個驗證都基于方法區的數據進行。

  • 元數據驗證:對字節碼描述的數據進行語義分析,以保證符合Java語言規范,如是否繼承了final修飾的類、是否實現了父類的抽象方法、是否覆蓋了父類的final方法或final字段等。

  • 字節碼驗證:對類的方法體進行分析,確保在方法運行時不會有危害虛擬機的事件發生,如保證操作數棧的數據類型和指令代碼序列的匹配、保證跳轉指令的正確性、保證類型轉換的有效性等。

  • 符號引用驗證:為了確保后續的解析動作能夠正常執行,對符號引用進行驗證,如通過字符串描述的全限定名是都能找到對應的類、在指定類中是否存在符合方法的字段描述符等。

  • 準備

    在準備階段,為類變量(static修飾)在方法區中分配內存并設置初始值。

    private static int var = 100;

    準備階段完成后,var 值為0,而不是100。在初始化階段,才會把100賦值給val,但是有個特殊情況:

    private static final int VAL= 100;

    在編譯階段會為VAL生成ConstantValue屬性,在準備階段虛擬機會根據ConstantValue屬性將VAL賦值為100。

    解析

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

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

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

    初始化

    初始化階段是執行類構造器方法的過程,方法由類變量的賦值動作和靜態語句塊按照在源文件出現的順序合并而成,該合并操作由編譯器完成。

    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、方法對于類或接口不是必須的,如果一個類中沒有靜態代碼塊,也沒有靜態變量的賦值操作,那么編譯器不會生成;?

    2、方法與實例構造器不同,不需要顯式的調用父類的方法,虛擬機會保證父類的優先執行;?

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

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

    類初始化場景

    虛擬機中嚴格規定了有且只有5種情況必須對類進行初始化。?

    1、執行new、getstatic、putstatic和invokestatic指令;?

    2、使用reflect對類進行反射調用;?

    3、初始化一個類的時候,父類還沒有初始化,會事先初始化父類;?

    4、啟動虛擬機時,需要初始化包含main方法的類;?

    5、在JDK1.7中,如果java.lang.invoke.MethodHandler實例最后的解析結果REFgetStatic、REFputStatic、REF_invokeStatic的方法句柄,并且這個方法句柄對應的類沒有進行初始化;

    以下幾種情況,不會觸發類初始化?

    1、通過子類引用父類的靜態字段,只會觸發父類的初始化,而不會觸發子類的初始化。

    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); } }

    輸出結果為: parent init! 100

    2、定義對象數組,不會觸發該類的初始化。

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

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

    ?anewarray指令為新數組分配空間,并觸發[Lcom.ctrip.ttd.whywhy.Parent類的初始化,這個類由虛擬機自動生成。

    3、常量在編譯期間會存入調用類的常量池中,本質上并沒有直接引用定義常量的類,不會觸發定義常量所在的類。

    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?
    說明沒有觸發類Const的初始化,在編譯階段,Const類中常量A的值100存儲到Init類的常量池中,這兩個類在編譯成class文件之后就沒有聯系了。

    4、通過類名獲取Class對象,不會觸發類的初始化。

    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");}

    執行結果:Cat is load

    所以通過Dog.class并不會觸發Dog類的初始化動作。

    5、通過Class.forName加載指定類時,如果指定參數initialize為false時,也不會觸發類初始化,其實這個參數是告訴虛擬機,是否要對類進行初始化。

    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默認的loadClass方法,也不會觸發初始化動作

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

    ?

    ?

    ?

    總結

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

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。