代碼
看下面代碼示例。思考程序的輸出內(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)檫@些方法都不能被覆蓋