一道簡單的題目
看到這個標(biāo)題時可能很多朋友會嗤之以鼻,難不成簡單的訪問修飾符還有什么新花樣嗎?別急,麻煩您先看一下這個簡單的題目。

這無非就是一個簡單運行時多態(tài)問題,眾所周知,Java的多態(tài)分為編譯時多態(tài)和運行時多態(tài)兩種。其中, 編譯時多態(tài)主要指方法的重載,運行時多態(tài)指程序中定義的對象引用所指向的具體類型在運行期間才確定。 運行時多態(tài)有三個條件:
繼承
方法重寫(覆蓋)
向上轉(zhuǎn)型
main函數(shù)中,base指向的實際上是Impl對象,因此在調(diào)用sayHi()方法時,會執(zhí)行Impl對象的sayHi()方法而非Base對象的sayHi()方法,因此,輸出的內(nèi)容顯然是Impl:Hi。
實際上是這樣的嗎?然而很不幸,輸出的卻是 Base:Hi。問題出在哪兒?難道這不滿足運行時多態(tài)的條件?不應(yīng)該啊,首先,Impl類繼承了Base類,其次,Impl類重寫了Base類的sayHi()方法,最后,調(diào)用時進(jìn)行了向上轉(zhuǎn)型。三個貌似條件都滿足,但是等等,回憶一下方法重寫的要求:
子類方法的訪問權(quán)限必須大于等于父類方法;
-
子類方法的返回類型必須是父類方法返回類型或為父類方法返回類型的子類型。
貌似也滿足啊,我們再看一下JLS中對方法重寫的規(guī)定:

翻譯過來大概為:
A是C的超類
C沒有繼承mA
mC是mA的簽名的子簽名
-
下面的多個條件之一要滿足:
mA是public的
mA是protected的 mA在C所處的包中具有包訪問權(quán)限,且mC覆蓋了來自C的某個超類中的mA
...訪問修飾符
看來我們忽略了方法的訪問修飾符的問題。Java中訪問修飾符規(guī)定及其訪問范圍如下表所示:
訪問權(quán)限 當(dāng)前類 同包 子類 其他包 public √ √ √ √ protected √ √ √ × default √ √ × × private √ × × × 那么,當(dāng)子類位于當(dāng)前類內(nèi)部、同一包下、其他包下時訪問權(quán)限會發(fā)生什么變化呢?是否還遵循表格中的規(guī)定呢?對于這個問題,我們只需要記住最大訪問權(quán)限原則即可,所謂最大訪問權(quán)限原則即子類的在不同位置時訪問權(quán)限修飾符表現(xiàn)的實際權(quán)限以最大的那個為準(zhǔn)。依據(jù)該原則,子類在不同位置時對父類中的方法及變量的訪問權(quán)限如下表所示:
子類中訪問權(quán)限 當(dāng)前類 同包 其他包 public √ √ √ protected √ √ √ default √ √ × private √ × × 回到題目中,由于子類Impl與父類Base位于同一包下,而Base中的sayHi()方法的修飾符為private,對子類不可見,因此不滿足方法重寫的要求,因此調(diào)用的仍然是Base中的方法,而非子類中的方法。
這個細(xì)節(jié)看起來很不起眼,但實際上卻包含了訪問修飾符的權(quán)限及方法重寫、多態(tài)等細(xì)節(jié),算得上是一道好題。