[短文速讀-3] 實例化子類會實例化父類麽
這是本系列的第三篇文章:本篇文章討論一個很簡單的問題:實例化子類是否會實例化父類。
本篇篇幅不長,適合碎片化時間閱讀。文章會從字節碼和dump內存結構來解答這個問題。
出場角色
小A:剛踏入Java編程之路…
MDove:一個快吃不上飯的Android開發…
正題
引子
小A:MDove,我最近在思考一個問題:實例化子類,會實例化父類麽?
MDove:這是一個有趣的問題。那我也反問你一個問題:抽象類可以被實例化麽?
小A:雖然我很菜,但你請不要羞辱我!抽象類不能被實例化這是常識啊。
MDove:既然你知道這個,那我再問你:子類繼承了抽象類。此時,實例化子類,那麽父類作為抽象類你覺得能被實例化麽?
小A:不能吧...
MDove:沒錯,就是如此啊。那你為什麽還會有疑問呢?
疑惑點
小A:因為我在學習多態的時候,發現new子類,父類的構造方法也會執行,就像這樣:
```java‘
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);
}
}
![](https://user-gold-cdn.xitu.io/2018/9/6/165af79252e20672?w=290&h=67&f=png&s=4956) **小A**:既然父類的構造方法都被執行了,那豈不也會實例化? ## 大錯特錯,父類不會實例化 **MDove**:有這樣的疑問,屬實不怪你。首先指出你一個錯誤:**構造方法被執行不代表實例化這個類**。為什麽構造方法會被執行?那是因為要初始化父類變量。 **MDove**:我們來看一下這個demo的字節碼: ![](https://user-gold-cdn.xitu.io/2018/9/9/165bdddbd340a35b?w=847&h=245&f=png&s=10555) **MDove**:看到紅線的指令了吧?子類的構造方法裏,調用了父類的構造方法。**子類需要繼承父類的public/protect類型的變量和方法。有繼承,那麽勢必要調用構造方法對其進行內存分配**。 **小A**:**那如果這麽說的話?如果父類是一個空實現,是不是就不用調用父類的構造方法了?** **MDove**:不會的,編譯期會幫我們隱式的調用。 **MDove**:我最開始也有這個疑惑,既然父類都沒有對變量進行賦值,為啥還這麽費勁的調用它的構造方法?沒錯,我們的父類可能不需要初始化變量,但是!!它的父類不一定不需要啊!!別忘了,我們所有的類都隱式的繼承了Object,你告訴我,**怎麽可以做到跳過調用父類的構造方法,而直接調用Object的構造方法**?很顯然,不好實現,因此逐級調用父類構造方法是一個合適的解決方案! **小A**:這麽一說還真是這回事。 ## 從內存分配角度看構造方法 **MDove**:為了避免文字的蒼白。這裏我使用一些工具,讓你看一看對象的內存分配。 > 這裏使用的工具是AndroidStudio3.0,因為我是一名Android開發,而且AS3.0在內存查看方面做的很出色,所以就用這個工具來展開這個內容。 **MDove**:我簡單新建了一個工程,很簡單的一個操作: ```java findViewById(R.id.btn_ok).setOnClickListener(new View.OnClickListener() { ` @Override public void onClick(View v) { new SunClass(); } });
MDove:接下來讓我們在Android Profiler裏邊看一些我們的內存分配情況,這裏我Dump了new子類的那個過程:
MDove:看到了吧?我們的包下面只有這麽3個主要內存對象。一個是我們MainActivity這個無需多言。MainActivity$1是我們的Listener。而SunClass也就是我們new的子類。很明顯,沒有父類!那讓我們來看一看SunClass的內存都分配了些啥:
MDove:看沒看到父類的變量:mSuperName!而且在SunClass的對象裏。清晰了吧?父類構造方法的執行,是為了給mSuperName這個變量進行賦值,而不是為了實例化父類。
小A:那如果我不在構造方法裏邊給變量賦值咋辦?比如這樣:
public class TestClass {
public String name = "TestClass";
public TestClass() {
}
}
MDove:你口中所謂的不在構造方法裏邊賦值,只是java層面的而已。我們看一看字節碼就清楚了:
MDove:看到了吧?我們的java文件在被編譯成class文件時。我們的編譯器會把對應成員變量初始化操作的字節碼寫到構造方法裏邊,就醬~
小A:那我學習道路常遇到的一些嘗試,比如:1、如果父類沒有顯示聲明一個無參構造方法,編譯器會隱式的增加一個無參構造方法。2、如果我們不顯式的調用父類的構造方法,編譯器會隱式的幫我們調用。是不是也是通過你說的這種形式去做?
MDove:沒錯,就醬~
小A:看樣子,學習的道路上不能想當然,有些內容還要多思考,多實踐才多~
劇終
[短文速讀-3] 實例化子類會實例化父類麽