
1 對象的執行個體化過程
- 對象的執行個體化過程是分成兩部分:類的加載初始化,對象的初始化
- 要建立類的對象執行個體需要先加載并初始化該類,main方法所在的類需要先加載和初始化
- 類初始化就是執行<clinit>方法,對象執行個體化是執行<init>方法
- 一個子類要初始化需要先初始化父類
2 類的加載過程
- 類的加載機制:如果沒有相應類的class,則加載class到方法區。對應着加載->驗證->準備->解析-->初始化階段
- 加載:載入class對象,不一定是從class檔案擷取,可以是jar包,或者動态生成的class
- 驗證:校驗class位元組流是否符合目前jvm規範
- 準備:為 類變量 配置設定記憶體并設定變量的初始值( 預設值 )。如果是final修飾的對象則是指派聲明值
- 解析:将常量池的符号引用替換為直接引用
- 初始化:執行類構造器<client>( 注意不是對象構造器 ),為 類變量 指派,執行靜态代碼塊。jvm會保證子類的<client>執行之前,父類的<client>先執行完畢
- 其中驗證、準備、解析3個部分稱為 連接配接
- <clinit>方法由 靜态變量指派代碼和靜态代碼塊 組成;先執行類靜态變量顯示指派代碼,再到靜态代碼塊代碼
3 觸發類加載的條件
- 第一次建立類的新對象時, 會觸發類的加載初始化和對象的初始化函數<init>執行,這個是執行個體初始化,其他6個都是類初始化
- JVM啟動時會先加載初始化包含main方法的類
- 調用類的靜态方法(如執行invokestatic指令)
- 對類或接口的靜态字段執行讀寫操作(即執行getstatic、putstatic指令);不過final修飾的靜态字段的除外(已經指派,String和基本類型,不包含包裝類型),它被初始化為一個編譯時常量表達式
- 注意 :操作靜态字段時,隻有直接定義這個字段的類才會被初始化;如通過其子類來操作父類中定義的靜态字段,隻會觸發父類<clinit>的初始化而不是子類的初始化
- 調用JavaAPI中的反射方法時(比調用java.lang.Class中的方法(Class.forName),或者java.lang.reflect包中其他類的方法)
- 當初始化一個類時,其父類沒有初始化,則需先觸發父類的初始化(接口例外)
4 對象的執行個體化過程
- 對象執行個體化過程其實就是執行類構造函數 對應在位元組碼檔案中的<init>()方法(稱之為執行個體構造器);<init>()方法由 非靜态變量、非靜态代碼塊以及對應的構造器組成
- <init>()方法可以重載多個,類有幾個構造器就有幾個<init>()方法
- <init>()方法中的代碼執行順序為:父類變量初始化,父類代碼塊,父類構造器,子類變量初始化,子類代碼塊,子類構造器。
- 靜态變量,靜态代碼塊,普通變量,普通代碼塊,構造器的執行順序
- 具有父類的子類的執行個體化順序如下
5 類加載器和雙親委派規則,如何打破雙親委派規則
- 類加載器
- 通過一個類的全限定名來擷取 描述此類的二進制位元組流 ,實作這個動作的代碼子產品稱為類加載器
- 任意一個類都需要其加載器和類本身來确定類在JVM的唯一性;每個類加載器都有自己的類名稱空間,同一個類class由不同的加載器加載,則被JVM判斷為不同的類
- 雙親委派模型
- 啟動類加載器有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
原作者:程式猿knight
原文連結:詳解JAVA對象執行個體化過程
原出處:公衆号
侵删