面試題:下面程序的運行結果

摘自:https://zhidao.baidu.com/question/489562987146637292.html

下面程序的運行結果是什麼?

public class Dervied extends Base {

private String name = "dervied";

    public Dervied() {
        tellName();
        printName();
    }

    public void tellName() {
        System.out.println("Dervied tell name: " + name);
    }

    public void printName() {
        System.out.println("Dervied print name: " + name);
    }

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

class Base {

    private String name = "base";

    public Base() {
        tellName();
        printName();
    }

    public void tellName() {
        System.out.println("Base tell name: " + name);
    }

    public void printName() {
        System.out.println("Base print name: " + name);
    }
}

輸出:

Dervied tell name: null
Dervied print name: null
Dervied tell name: dervied
Dervied print name: dervied

關鍵點:問題出在構造函數的地方,對於java中的extends,每個子類構造方法的第一條語句都是隱含的調用super,而且如果父類沒有這種形式的構造函數就會報錯。

詳細解釋
1,首先先說一下父類與子類方法覆寫(overrider)相關的概念,覆寫在java中主要是通過方法表來完成,java中每個類型(Class)中都存在一個方法表,其中存放java類型的實例方法(public/protected),static方法屬於靜態方法,和類型相關,不屬於javad實例方法。private和default屬於私有方法,private/default修飾的方法不進入類型的方法表。

2,接下來說一下和方法,其中稱爲類初始化方法,稱爲對象實例化方法,該問題就與方法息息相關。
方法:
Java在進行對象創建時首先進行類型加載,如果Class類型存在父類型,則需要先加載父類完成以後再加載子類型。並且對static變量進行初始化操作,對static變量或者static代碼塊初始化的邏輯就封裝在方法中。
Java類型初始化過程中對static變量的初始化操作依賴與對static變量的賦值語句的前後關係,static語句塊與static變量聲明存在位置關係,java編譯器與static變量的賦值位置有關。
方法:
Java對象在進行實例化時,首先會進行父類的實例化操作,然後再進行子類的實例化操作。該部分操作封裝在方法中,並且子類的方法中會首先對父類方法的調用。
Java對象實例化過程中對實例域的初始化賦值操作全部在方法中進行,方法顯式的調用父類的方法,實例域的聲明語句以及實例初始化語句塊存在位置關係,方法以構造方法作爲結束。

接下來回到這道題上來,啓動java虛擬機:
第一步:加載應用用到的類,加載順序爲Base–>Dervied,這裏涉及到方法的調用,但是當前類型中沒有static域或者是static{},所以方法不再討論。

第二步:對象實例化操作,首先我們new Dervied();,這樣就觸發對Dervied.的調用,但是根據以上知識(“並且子類的方法中會首先對父類方法的調用”)我們可以知道,Dervied.首先會調用到Base.方法,所以現在我們調用的方法是Base.,Base.方法中,首先是對name域(父類Base)進行初始化,然後調用tellName()方法,因爲子類以及覆寫了tellName()方法,所以tellName方法實際調用爲Dervied.tellName()方法,tellName方法要打印name域(子類Dervied)的值,但是當前Dervied對象中的name域還沒有被初始化,所以打印出來的值爲null。printName()方法執行與tellName()方法一致。

第三步:Base.方法返回到Dervied.方法中,緊接着對name域(子類Dervied)進行初始化操作,然後調用子類Dervied的tellName方法和printName方法,name域(子類Dervied)已經初始化完成,所以能正確打印出來。

發佈了57 篇原創文章 · 獲贊 6 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章