深入解析Java對象的初始化過程【看完這篇,你爸爸再也不用擔心你不會實例Java 對象了】


這還是從一道Java 的面試題說起。不多說直接看這道面試題:

public class Base{

  private String baseName = "base";

  //構造方法

  public Base(){callName();}

  //對象方法

  public void callName(){

    System. out. println(baseName);

  }

  //靜態內部類

   static class Sub extends Base{

//靜態內部類的字段

  private String baseName = "sub";

  //靜態類中的方法,可以發現,是對父類中方法的重寫

  public void callName(){

    System. out. println (baseName) ;

  }

  }

//程序的入口

  public static void main(String[] args){

    Base b = new Sub();

  }

}

 求這段程序的輸出。

【良言一句:同志們,別小看了這部分代碼,接下來慢慢解析從類加載器開到最后的輸出這么個漫長的過程JVM他是怎么處理的】

1、我們還是先從類加載開始說起。當這個類被編譯通知后,會在相應的目錄下生成兩個.class 文件。一個是 Base.class,另外一個就是Base$Sub.class。這個時候類加載器將這兩個.class  文件加載到內存

2、靜態代碼塊優先執行,因此先執行Sub  類中的代碼,Sub類中沒有靜態代碼塊

【注意-1】先后順序,是先子在--->在父類,【注意-2】字段的值是放在構造器中,按代碼順序執行進行初始化操作

3、一切初始化(字節碼文件該加載的都加載完成)完畢,進入main方法--看到這里的童鞋們,千萬別眨眼,關鍵的地方上演了--左邊Base b,在這里的代碼中,等於就是說了一句廢話,直接可跳躍,從 new Sub()開始,這個時候會調用Sub類的隱式構造函數。這個隱式構造函數是JVM無償拱手讓給你的。還原Sub  類中的構造函數的本質如下:

 public Sub(){

  super();//始終在構造函數中的第一行,為什么呢?這是因為在一些軟件的升級中,要兼容老版本的一些功能,父類即原先的一些初始化信息也要保證被執行到,再執行當前

  baseName = "sub";

}

4、好了,這個時候執行super()這行代碼也就是跑到父類中去執行了,這個我們要到父類中的構造方法中來瞧瞧。public Base(){callName();},同樣,我們需要需要還原這段代碼的本質:

public Base(){

  baseName = "base";//4.1 java里面沒有字段的重寫,只有方法名的重寫,因此這個時候先給父類的字段baseName 分配好內存空間再給baseName 字段進行的賦值   

  callName();//4.2 callName()方法的調用。這里有一個執行細節需要大家注意:當在父類中方法的執行時,執行的原則是:子類有重寫,執行子類,子類沒有重寫,則在執行父類)。這個時候調用的就是子類的callName 方法了,

}

5、子類的callName方法執行,打印輸出的是子類的baseName 字段的值,而這個時候子類的構造函數中字段的賦值還未執行,因此這個時候輸出的null

6、父類的構造函數執行完畢,這個時候又回到子類當中,從super()的下一行繼續執行,這個時候才進行該字段baseName 分配好存儲空間,隨后賦其值。

這個過程的調用時序圖如下所示:

你看,在第四步的時候還未進行初始化,就先打印其字段值,很顯然這個時候輸出的null。

最后附上其輸出結果:

【謝謝觀賞,此篇完畢】

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM