[短文速度-4] new子類是否會例項化父類

MDove發表於2018-09-14

前言

本篇文章討論一個很簡單的問題:例項化子類是否會例項化父類。

本篇篇幅不長,適合碎片化時間閱讀。文章會從位元組碼dump記憶體結構來解答這個問題。

愛因斯坦:“如果你不能簡單地解釋一樣東西,說明你沒真正理解它。”

[短文速讀-1] a=a+b和a+=b的區別

[短文速讀-2] 過載/重寫,動/靜態分派?(重新修訂)

[短文速讀-3] 內部匿名類使用外部變數為什麼要加final

[短文速度-4] new子類是否會例項化父類

[短文速讀 -5] 多執行緒程式設計引子:程式、執行緒、執行緒安全

出場角色

小A:剛踏入Java程式設計之路…

MDove:一個快吃不上飯的Android開發…

正題

引子

小A:MDove,我最近在思考一個問題:例項化子類,會例項化父類麼?

MDove:這是一個有趣的問題。那我也反問你一個問題:抽象類可以被例項化麼?

小A:雖然我很菜,但你請不要羞辱我!抽象類不能被例項化這是常識啊。

MDove:既然你知道這個,那我再問你:子類繼承了抽象類。此時,例項化子類,那麼父類作為抽象類你覺得能被例項化麼?

小A:不能吧...

MDove:沒錯,就是如此啊。那你為什麼還會有疑問呢?

疑惑點

小A:因為我在學習多型的時候,發現new子類,父類的構造方法也會執行,就像這樣:

public static void main(String[] args) {
    SunClass sunClass1 = new SunClass();
}

public class SunClass extends SuperClass {
    public String mSunName;
    public SunClass() {
        mSunName = "Sun Name";
        System.out.println(mSunName);
    }
}

public class SuperClass {
    public String mSuperName;
    public SuperClass() {
        mSuperName = "Super Name";
        System.out.println(mSuperName);
    }
}
複製程式碼

image

小A:既然父類的構造方法都被執行了,那豈不也會例項化?

大錯特錯,父類不會例項化

MDove:有這樣的疑問,屬實不怪你。首先指出你一個錯誤:構造方法被執行不代表例項化這個類。為什麼構造方法會被執行?那是因為要初始化父類變數。

MDove:我們來看一下這個demo的位元組碼:

image

MDove:看到紅線的指令了吧?子類的構造方法裡,呼叫了父類的構造方法。子類需要繼承父類的public/protect型別的變數和方法。有繼承,那麼勢必要呼叫構造方法對其進行記憶體分配

小A那如果這麼說的話?如果父類是一個空實現,是不是就不用呼叫父類的構造方法了?

MDove:不會的,編譯期會幫我們隱式的呼叫。

MDove:我最開始也有這個疑惑,既然父類都沒有對變數進行賦值,為啥還這麼費勁的呼叫它的構造方法?沒錯,我們的父類可能不需要初始化變數,但是!!它的父類不一定不需要啊!!別忘了,我們所有的類都隱式的繼承了Object,你告訴我,怎麼可以做到跳過呼叫父類的構造方法,而直接呼叫Object的構造方法?很顯然,不好實現,因此逐級呼叫父類構造方法是一個合適的解決方案!

小A:這麼一說還真是這回事。

從記憶體分配角度看構造方法

MDove:為了避免文字的蒼白。這裡我使用一些工具,讓你看一看物件的記憶體分配。

這裡使用的工具是AndroidStudio3.0,因為我是一名Android開發,而且AS3.0在記憶體檢視方面做的很出色,所以就用這個工具來展開這個內容。

MDove:我簡單新建了一個工程,很簡單的一個操作:

findViewById(R.id.btn_ok).setOnClickListener(new View.OnClickListener() {
`   @Override
    public void onClick(View v) {
        new SunClass();
    }
});
複製程式碼

MDove:接下來讓我們在Android Profiler裡邊看一些我們的記憶體分配情況,這裡我Dump了new子類的那個過程:

image

image

MDove:看到了吧?我們的包下面只有這麼3個主要記憶體物件。一個是我們MainActivity這個無需多言。MainActivity$1是我們的Listener。而SunClass也就是我們new的子類。很明顯,沒有父類!那讓我們來看一看SunClass的記憶體都分配了些啥:

image

MDove:看沒看到父類的變數:mSuperName!而且在SunClass的物件裡。清晰了吧?父類構造方法的執行,是為了給mSuperName這個變數進行賦值,而不是為了例項化父類

小A:那如果我不在構造方法裡邊給變數賦值咋辦?比如這樣:

public class TestClass {
    public String name = "TestClass";

    public TestClass() {
    }
}
複製程式碼

MDove:你口中所謂的不在構造方法裡邊賦值,只是java層面的而已。我們看一看位元組碼就清楚了:

image

MDove:看到了吧?我們的java檔案在被編譯成class檔案時。我們的編譯器會把對應成員變數初始化操作的位元組碼寫到構造方法裡邊,就醬~

小A:那我學習道路常遇到的一些嘗試,比如:1、如果父類沒有顯示宣告一個無參構造方法,編譯器會隱式的增加一個無參構造方法。2、如果我們不顯式的呼叫父類的構造方法,編譯器會隱式的幫我們呼叫。是不是也是通過你說的這種形式去做?

MDove:沒錯,就醬~

小A:看樣子,學習的道路上不能想當然,有些內容還要多思考,多實踐才多~

劇終

我是一個應屆生,最近和朋友們維護了一個公眾號,內容是我們在從應屆生過渡到開發這一路所踩過的坑,以及我們一步步學習的記錄,如果感興趣的朋友可以關注一下,一同加油~

個人公眾號:IT面試填坑小分隊

相關文章