基礎篇:詳解JAVA對象實例化過程

1 對象的實例化過程

  • 對象的實例化過程是分紅兩部分:類的加載初始化,對象的初始化
  • 要建立類的對象實例須要先加載並初始化該類,main方法所在的類須要先加載和初始化
  • 類初始化就是執行<clinit>方法,對象實例化是執行<init>方法
  • 一個子類要初始化須要先初始化父類

2 類的加載過程

  • 類的加載機制:若是沒有相應類的class,則加載class到方法區。對應着加載->驗證->準備->解析-->初始化階段html

    • 加載:載入class對象,不必定是從class文件獲取,能夠是jar包,或者動態生成的class
    • 驗證:校驗class字節流是否符合當前jvm規範
    • 準備:爲類變量分配內存並設置變量的初始值(默認值)。若是是final修飾的對象則是賦值聲明值
    • 解析:將常量池的符號引用替換爲直接引用
    • 初始化:執行類構造器<client>(注意不是對象構造器),爲類變量賦值,執行靜態代碼塊。jvm會保證子類的<client>執行以前,父類的<client>先執行完畢
  • 其中驗證、準備、解析3個部分稱爲 鏈接
  • <clinit>方法由靜態變量賦值代碼和靜態代碼塊組成;先執行類靜態變量顯示賦值代碼,再到靜態代碼塊代碼

3 觸發類加載的條件

  • 第一次建立類的新對象時,會觸發類的加載初始化和對象的初始化函數<init>執行,這個是實例初始化,其餘6個都是類初始化
  • JVM啓動時會先加載初始化包含main方法的類
  • 調用類的靜態方法(如執行invokestatic指令)
  • 對類或接口的靜態字段執行讀寫操做(即執行getstatic、putstatic指令);不過final修飾的靜態字段的除外(已經賦值,String和基本類型,不包含包裝類型),它被初始化爲一個編譯時常量表達式java

    • 注意:操做靜態字段時,只有直接定義這個字段的類纔會被初始化;如經過其子類來操做父類中定義的靜態字段,只會觸發父類<clinit>的初始化而不是子類的初始化
  • 調用JavaAPI中的反射方法時(比調用java.lang.Class中的方法(Class.forName),或者java.lang.reflect包中其餘類的方法)
  • 當初始化一個類時,其父類沒有初始化,則需先觸發父類的初始化(接口例外)

4 對象的實例化過程

  • 對象實例化過程 其實就是執行類構造函數 對應在字節碼文件中的<init>()方法(稱之爲實例構造器);<init>()方法由非靜態變量、非靜態代碼塊以及對應的構造器組成app

    • <init>()方法能夠重載多個,類有幾個構造器就有幾個<init>()方法
    • <init>()方法中的代碼執行順序爲:父類變量初始化,父類代碼塊,父類構造器,子類變量初始化,子類代碼塊,子類構造器。
  • 靜態變量,靜態代碼塊,普通變量,普通代碼塊,構造器的執行順序

  • 具備父類的子類的實例化順序以下

5 類加載器和雙親委派規則,如何打破雙親委派規則

  • 類加載器jvm

    • 經過一個類的全限定名來獲取描述此類的二進制字節流,實現這個動做的代碼模塊稱爲類加載器
    • 任意一個類都須要其加載器和類自己來肯定類在JVM的惟一性;每一個類加載器都有本身的類名稱空間,同一個類class由不一樣的加載器加載,則被JVM判斷爲不一樣的類

  • 雙親委派模型ide

    • 啓動類加載器有C++代碼實現,是虛擬機的一部分。負責加載lib下的類庫
    • 其餘的類加載器有java語言實現,獨立於JVM,而且繼承ClassLoader
    • extention ClassLoader負責加載libext目錄下的類庫
    • application ClassLoader 負責加載用戶路徑下(ClassPath)的代碼
    • 不一樣的類加載器加載同一個class文件會致使出現兩個類。而java給出解決方法是下層的加載器加委託上級的加載器去加載類,若是父類沒法加載(在本身負責的目錄找不到對應的類),而交還下層類加載器去加載。以下圖

  • 打破雙親委派模型函數

    • 雙親委派模型並非一個強制的約束模型,而是java設計者推薦給開發者的類加載實現方式
    • 雙親委派模型很好的解決各個類加載基礎類的同一問題(越基礎的類由越上層的加載器加載),可是基礎類老是做爲用戶代碼調用的API,可是若是它的具體實現是下層的代碼,此時基礎類須要調用下層的代碼,則須要打破雙親委派模型
    • 如JNDI服務,JNDI的代碼有啓動類去加載(rt.jar),它須要調用由獨立廠商部署在應用程序classpath下的JNDI的SPI(Service Provider Interface)代碼。爲了解決SPI代碼加載問題,java引入了線程上下文類加載器去加載SPI代碼。也就是父類加載器請求子類去完成類的加載動做
    • 線程上下文類加載器,線程建立時會從父線程繼承,若是全局範圍沒有設置過,則默認設置爲application Class Loader

歡迎指正文中錯誤

關注公衆號,一塊兒交流

參考文章

相關文章
相關標籤/搜索