構(gòu)造器中絕對(duì)絕對(duì)不能調(diào)用可被覆蓋的方法

代碼

看下面代碼示例。思考程序的輸出內(nèi)容

import java.time.Instant;

class Super{
    public Super(){
        overrideMe();
    }
    public void overrideMe(){

    }
}
public class Student extends Super{

    private final Instant instant;

    Student(){
        instant = Instant.now();
    }

    @Override
    public void overrideMe(){
        System.out.println(instant);
    }

    public static void main(String[] args){
        Student stu = new Student();
        stu.overrideMe();
    }
}

程序的運(yùn)行結(jié)果是:

null
2020-05-13T02:51:01.398Z

思考:為什么兩次調(diào)用 overrideMe() 方法只有第二次成功輸出了instant對(duì)象

分析

首先我們分析一下代碼,代碼中有兩個(gè)類,Super 類和 Student 類,Super 類中有一個(gè) overrideMe()方法,方法體為空,Super 類的構(gòu)造器中調(diào)用了 overrideMe()方法。Student 繼承了 Super 類,Student 類中有一個(gè) Instant 類型的對(duì)象,在 Student 的構(gòu)造器中被實(shí)例化,同時(shí) Student 類覆蓋了 Super 類的 overrideMe()方法,在方法體中打印 instant 對(duì)象。我們?cè)?main 函數(shù)中通過(guò)調(diào)用 Student 的構(gòu)造器創(chuàng)建了 stu 對(duì)象,并且調(diào)用其overrideMe()方法。

繼續(xù)詳細(xì)的分析代碼↓↓

先從main函數(shù)下手

public static void main(String[] args){
    Student stu = new Student();
    stu.overrideMe();
}

在main方法中我們通過(guò) Student stu = new Student() 調(diào)用 Student 的構(gòu)造器,由于 Student 類繼承了 Super 類而 調(diào)用子類構(gòu)造器之前會(huì)先調(diào)用超類的構(gòu)造器,也就是在調(diào)用 Student 類的構(gòu)造器前會(huì)先調(diào)用 Super 類的構(gòu)造器↓↓

public Super(){
    overrideMe();
}

在超類構(gòu)造器中,又調(diào)用了 overrideMe()方法,此方法在 Super 中的方法體內(nèi)容為空,但是由于 overrideMe()方法在 Student 中被覆蓋了,實(shí)際上 Super 類構(gòu)造器調(diào)用的是 Student 類中 overrideMe()方法↓↓

@Override
public void overrideMe(){
    System.out.println(instant);
}

此方法打印 Student 中 instant 對(duì)象,但是此時(shí) Student 的構(gòu)造函數(shù)并沒(méi)有被調(diào)用過(guò),所以此此時(shí) instant 對(duì)象實(shí)際為 null,這也就是程序運(yùn)行結(jié)果中第一行打印 null 的原因。調(diào)用完此方法后,程序回到 Super 類構(gòu)造器中,超類構(gòu)造器執(zhí)行完成,此時(shí)才會(huì)調(diào)用子類(Student)的構(gòu)造器↓↓

Student(){
    instant = Instant.now();
}

在 Student 的構(gòu)造方法中通過(guò)調(diào)用 Instant.now() 方法(獲取當(dāng)前時(shí)間戳),實(shí)例化了 Student 中的 instant 對(duì)象,至此 Student 實(shí)例化完成,回到了main 方法↓↓

public static void main(String[] args){
    Student stu = new Student();
    stu.overrideMe();
}

實(shí)例化 Student 完成之后我們又調(diào)用了其 overrideMe() 方法↓↓

@Override
public void overrideMe(){
    System.out.println(instant);
}

此時(shí),instant 對(duì)象在剛才的 Student 實(shí)例化時(shí)也被實(shí)例化了,此時(shí)打印的結(jié)果就是我們?cè)趯?shí)例化時(shí)獲取到的時(shí)間戳了,這就是第二行輸出結(jié)果

回到主題,構(gòu)造器中不能調(diào)用可被覆蓋的方法,我們稍微改造一下 Student 中的 overrideMe()方法

@Override
public void overrideMe(){
    System.out.println(instant.getNano());
}

通過(guò)調(diào)用 instant 對(duì)象的 getNano()方法,我們意圖獲取時(shí)間戳的納秒表示。通過(guò)上面的分析,在第一次調(diào)用overrideMe()方法時(shí),instant對(duì)象為 null,所以我們可以獲得下面的輸出結(jié)果:

Exception in thread "main" java.lang.NullPointerException
        at Student.overrideMe(Student.java:21)
        at Super.<init>(Student.java:5)
        at Student.<init>(Student.java:15)
        at Student.main(Student.java:25)

沒(méi)錯(cuò)肯定會(huì)出現(xiàn)空指針異常!

這也就是為什構(gòu)造器中不能調(diào)用可被覆蓋的方法,因?yàn)閷?duì)于可覆蓋方法,在子類覆蓋過(guò)程中,很有可能會(huì)使用到子類中特有的屬性,而由于實(shí)例化順序的原因,在父類中調(diào)用被覆蓋了的方法,就很有可能會(huì)造成程序運(yùn)行失敗。

注:通過(guò)構(gòu)造器調(diào)用私有方法、final 方法靜態(tài)方法是安全的,因?yàn)檫@些方法都不能被覆蓋

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容