面試題——Java 類加載/創(chuàng)建對象的過程

簡潔回答:

  • 第一步,判斷常量池是否能定位類的符號引用,并且檢查這個符號引用代表的類是否被加載、解析、初始化過。如果沒有則執(zhí)行第一步,如果有則執(zhí)行第二步
  • 第二步類加載和初始化(初次使用),具體可分為加載、驗證、準備、解析、初始化
  • 第三步創(chuàng)建對象,具體可分為實例對象分配內(nèi)存、賦值、執(zhí)行實例初始化代碼、返回引用

詳細回答

第一步

判斷常量池是否能定位類的符號引用,并且檢查這個符號引用代表的類是否被加載、解析、初始化過。如果沒有則執(zhí)行第一步,如果有則執(zhí)行第二步

第二步,類加載和初始化(初次使用)

如果類已經(jīng)被加載過,則不會執(zhí)行第一步整個步驟

1. 加載
  • 類加載器根據(jù)類的全限定名來讀取此類的二進制字節(jié)流到JVM內(nèi)部,并存儲在運行時內(nèi)存區(qū)的方法區(qū),然后將其轉(zhuǎn)換為一個與目標類型對應(yīng)的java.lang.Class對象實例
2. 驗證
  • 格式驗證:驗證是否符合class文件規(guī)范,比如必須以魔數(shù)0xCAFEBABE開頭
  • 語義驗證:檢查一個被標記為final的類型是否包含子類;檢查一個類中的final方法是否被子類進行重寫;確保父類和子類之間沒有不兼容的一些方法聲明(比如方法簽名相同,但方法的返回值不同)
  • 操作驗證:在操作數(shù)棧中的數(shù)據(jù)必須進行正確的操作,對常量池中的各種符號引用執(zhí)行驗證(通常在解析階段執(zhí)行,檢查是否可以通過符號引用中描述的全限定名定位到指定類型上,以及類成員信息的訪問修飾符是否允許訪問等)
3. 準備
  • 為類中的所有靜態(tài)變量/常量分配內(nèi)存空間,并為其設(shè)置一個初始值(由于還沒有產(chǎn)生對象,實例變量不在此操作范圍內(nèi))

public static int value= 1,初始化后的值為 0

public static final int value= 1,初始化后的值為 1

4. 解析
  • 將常量池中的符號引用轉(zhuǎn)為直接引用(得到類或者字段、方法在內(nèi)存中的指針或者偏移量,以便直接調(diào)用該方法)

符號引用:比如我們規(guī)定了json字符串,"{}"表示對象,"[]"表示數(shù)組,"{}"和"[]"就相當(dāng)于符號引用。在JVM中符號引用是用一組符號描述所引用的目標,比如用0x4000聲明這是一個枚舉類型

直接引用:內(nèi)存地址(指針/偏移量/句柄)

5. 初始化(先父類,再子類)
  • 執(zhí)行類構(gòu)造器 init 方法,init方法包含 為靜態(tài)變量賦值、執(zhí)行static代碼塊

第三步,創(chuàng)建對象

1. 在堆區(qū)為實例對象分配內(nèi)存
  • 為實例變量分配內(nèi)存(包括本類和父類),但不包括任何靜態(tài)變量
2. 對實例變量賦默認值
  • 將方法區(qū)內(nèi)對實例變量的定義拷貝一份到堆區(qū),然后賦默認值
3. 執(zhí)行實例初始化代碼
  • 初始化父類再初始化子類,初始化時先執(zhí)行非靜態(tài)代碼塊(包括非靜態(tài)初始化塊,非靜態(tài)屬性)再執(zhí)行構(gòu)造方法

非靜態(tài)代碼塊也叫做實例代碼塊

4. 將堆區(qū)對象的地址賦值給棧區(qū)的引用變量
  • 有類似于Child c = new Child()形式的引用,將堆區(qū)對象的地址賦值給棧區(qū)的引用變量c


流程圖

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容