Java初始化過程

来源:http://www.cnblogs.com/qifengshi/archive/2017/01/06/6257350.html
-Advertisement-
Play Games

這篇文章主要講解Java在創建對象的時候,初始化的順序。主要從以下幾個例子中講解: 繼承關係中初始化順序 初始化塊與構造器的順序 已經載入過的類的初始化順序 載入父類,會不會載入子類 創建子類對象會不會創建父類對象 例子1——繼承關係中初始化順序 先看簡單的情況,看下麵的例子: 其執行的結果如下: ...


這篇文章主要講解Java在創建對象的時候,初始化的順序。主要從以下幾個例子中講解:

  • 繼承關係中初始化順序
  • 初始化塊與構造器的順序
  • 已經載入過的類的初始化順序
  • 載入父類,會不會載入子類
  • 創建子類對象會不會創建父類對象

例子1——繼承關係中初始化順序

先看簡單的情況,看下麵的例子:

public class Father {

    public String fatherVar = "父類構造塊初始化";
    public static int fatherStaticVar;
    public int i;
    static {
        int i = 100;
        System.out.println("父類靜態塊初始化,i的值為" + i);
        System.out.println("父類靜態變數初始化,fatherStaticVar的值為" + fatherStaticVar);
    }

    {
        System.out.println(fatherVar);
    }

    public Father(){
        System.out.println("父類構造函數的初始化,i的值" + i);
    }
}

public class Son extends Father {

    public String sonVar = "子類構造塊初始化";
    public static int sonStaticVar;
    public int i;
    static {
        int i = 101;
        System.out.println("子類靜態塊初始化,i的值為" + i);
        System.out.println("子類靜態變數初始化,sonStaticVar的值為" + sonStaticVar);
    }

    {
        System.out.println(sonVar);
    }

    public Son(){
        super();
        System.out.println("子類構造函數的初始化,i的值" + i);
    }

    public static void main(String[] args) {
        new Son();
    }
}

其執行的結果如下:

父類靜態塊初始化,i的值為100
父類靜態變數初始化,fatherStaticVar的值為0
子類靜態塊初始化,i的值為101
子類靜態變數初始化,sonStaticVar的值為0
父類構造塊初始化
父類構造函數的初始化,i的值0
子類構造塊初始化
子類構造函數的初始化,i的值0

按照結果,我們可以知道在有繼承的時候,雖然是創建一個Son對象,但是JVM發現Son對象的類還沒有裝載,而Son類又繼承自Father類,只有載入了Father類,才能載入Son類。於是載入Father類的時候,就會初始化一切靜態變數和靜態塊。所以上文結果中第一行和第二行是父類靜態變數和靜態塊初始化的結果,然後載入完Father類之後,又會載入Son類,同樣是初始化Son類的靜態塊和靜態變數,出現上文中第三行和第四行的結果。等這個2個類都載入完了,才開始創建Son對象,因為Son對象,顯示調用了Father類的構造器,所以先執行Father類的構造器,出現第五行和第六行的結果,等Father類構造器執行完了,才執行後續Son構造器的內容,所以最後出現了第七行和第八行的結果。

例子2——初始化塊與構造器的順序

在上面的例子中,有2個語句塊叫初始化塊。在上文的結果中是初始化塊的執行是先於構造器的,現在看一下把初始化塊的內容放到構造器下麵,會是什麼的結果

public class InitBlock {

    public InitBlock(){
        System.out.println("構造器在執行......");
    }

    {
        System.out.println("初始化塊1在執行......");
    }

    {
        System.out.println("初始化塊2在執行......");
    }

    public static void main(String[] args) {
        new InitBlock();
    }
}

結果如下:

初始化塊1在執行......
初始化塊2在執行......
構造器在執行......

很顯然,無論初始化塊寫在哪個地方,都是先於構造器執行的,但是初始化塊之間的順序是前面的先初始化,後面在初始化。

例子3——已經載入過的類的初始化順序

更改一下例子1中的main方法,改成如下:

public static void main(String[] args) {
        new Father();
        System.out.println("=============");
        new Son();
    }

結果如下:

父類靜態塊初始化,i的值為100
父類靜態變數初始化,fatherStaticVar的值為0
子類靜態塊初始化,i的值為101
子類靜態變數初始化,sonStaticVar的值為0
父類構造塊初始化
父類構造函數的初始化,i的值0
=============
父類構造塊初始化
父類構造函數的初始化,i的值0
子類構造塊初始化
子類構造函數的初始化,i的值0

結果很有意思,創建父類對象的時候,載入Father類,出現第一行和第二行的結果,但是這個竟然會還把子類的靜態變數和靜態塊初始化?這個原因,例子4在說。 最後執行父類的構造器創建父類對象。當再創建子類的時候,發現父類和子類已經載入過了,所以不會再載入Father和Son類,只會調用父類的構造器,再執行後續子類構造器的內容,創建子類。

例子4——載入父類,會不會載入子類

用一個嶄新的例子來看看上面,創建父類的時候,為什麼會列印出子類靜態初始化執行的結果。

public class StaticFather {
    static{
        System.out.println("父類靜態初始化塊");
    }
}

public class StaticSon extends StaticFather{
    static {
        System.out.println("子類靜態初始化塊");
    }
}

public class Test {

    public static void main(String[] args) {
        new StaticFather();
    }
}

結果如下:

父類靜態初始化塊

這次就不會創建父類的時候,載入子類。例子3之所以出現這個原因 是因為main函數在子類中寫的,要執行main函數必須要載入子類。只會載入子類之前要先載入父類,因為不載入父類,只載入子類,怎麼讓子類調用父類的方法和變數。但是載入父類不會載入子類,反正父類也調用不了子類的方法。

例子5——創建子類對象會不會創建父類對象

做個實驗,看一下創建子類對象的時候,到底會不會創建一個父類對象,先說結論:不會。從道理上講,如果創建任何一個對象都要創建出一個他的父類對象的話,那麼整個JVM虛擬機都是Object對象。看下麵的實驗:

public class ObjectFather {

    public void getInfo(){
        System.out.println(getClass().toString());
    }
}

public class ObjectSon extends ObjectFather{

    public ObjectSon(){
        super();
        super.getInfo();
    }

    public static void main(String[] args) {
        new ObjectSon();
    }
}

結果如下:

class com.byhieg.init.ObjectSon

可以看出來,創建子類對象時那個父類的Class還是子類的,也就是說創建子類對象並沒有創建一個父類的對象,只是說調用了父類的構造器,對父類的屬性進行初始化,並且給子類提供了一個super指示器去調用父類中那些變數和方法。
更詳細的說,new一個對象實際上是通過一個new指令開闢一個空間,來存放對象。在new ObjectSon()的時候,就只有一個new指令,只會開闢一個空間,所謂初始化父類等等,都是在這個空間中有一個特殊的區域來存放這些數據,而super關鍵字就是提供了訪問這個特殊區域的方法,通過super去訪問這個特殊區域。
還可以比較super和this的hashcode來判斷,結果必然是兩者的hashcode是一致的。

總結

至此,Java初始化的講解到結束了,基本了覆蓋了絕大多數情況中的初始化。



您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • EC筆記:第4部分:22、所有成員都應該是private的 更簡單的訪問 用戶不用記得什麼時候該帶上括弧,什麼時候不用帶上括弧(因為很確定的就要帶上括弧) 訪問限制 對於public的成員變數,我們可以隨意對其設置值(無論合法還是非法,它都會無條件接受) 但是我們如果將其設置為private的,那麼... ...
  • abalone.data.rar ...
  • Python基礎教程 網易雲課堂-零基礎入門學習Python ...
  • 1. 建庫連庫 連接MySQL資料庫需要安裝支持 npm install mysql 我們需要提前安裝按mysql sever端 建一個資料庫mydb1 然後建一張表user如下 接下來我們利用nodejs連接mysql資料庫 但是實際每次創建連接都需要一定的開銷,執行效率就會有影響。下麵介紹一種連 ...
  • 1, 在scala REPL中輸入3. 然後按下tab鍵,有哪些方法可以被調用? 24個方法可以被調用, 8個基本類型; 基本的操作符, 等; 2,在scala REPL中,計算3的平方根,然後對該值求平方,現在這個結果跟3相差多少? scala.math.sqrt(3) res17: Double ...
  • 總覺的自己還有好多的東西還不懂,便很急促的學這學那沒有方向,殊不知實質浪費了多少時間。 今天學習了多線程,來總結一下 多線程基礎 先聊一下什麼叫做進程。 在操作系統中,同時有許多個進程在運行,他們依照優先順序排列,依次占用資源來運行。一個進程由多個線程組成。Java在運行的時啟動JVM,每個JVM相當 ...
  • 方法一:access函數判斷文件夾或者文件是否存在 函數原型: int access(const char *filename, int mode); 所屬頭文件:io.h filename:可以填寫文件夾路徑或者文件路徑 mode:0 (F_OK) 只判斷是否存在 2 (R_OK) 判斷寫入許可權 ...
  • 大圖輪播 > 1 2 ... ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...