重載(Overload)和重寫(Override)的區(qū)別宛乃。重載的方法能否根據(jù)返回類型進(jìn)行區(qū)分

面試題: 重載(Overload)和重寫(Override)的區(qū)別。重載的方法能否根據(jù)返回類型進(jìn)行區(qū)分

面試官考察點(diǎn)猜想

這道題純粹只是考查基礎(chǔ)理論知識(shí),對(duì)實(shí)際開發(fā)工作中沒有太多的指導(dǎo)意義碟绑,畢竟編輯器都有語法提示功能,如果沒寫正確茎匠,會(huì)有錯(cuò)誤提示格仲。

背景知識(shí)詳解

關(guān)于重載(Overload)和重寫(Override),在實(shí)際開發(fā)中使用非常頻繁诵冒,涉及到的背景知識(shí)并不難凯肋。

重寫

重寫是子類對(duì)父類的允許訪問的方法的實(shí)現(xiàn)過程進(jìn)行重新編寫, 返回值和形參都不能改變。即外殼不變汽馋,核心重寫侮东!

重寫是發(fā)生在類的繼承關(guān)系,或者類的實(shí)現(xiàn)關(guān)系中的豹芯,重寫后的方法和原方法需要保持完全相同的返回值類型悄雅、方法名、參數(shù)個(gè)數(shù)以及參數(shù)類型铁蹈,簡單來說宽闲,就是子類重寫的方法必須和父類保持完全一致

類的繼承關(guān)系

我們來看下面這個(gè)基于繼承關(guān)系的例子。

class Animal{
   public void move(){
      System.out.println("動(dòng)物可以移動(dòng)");
   }
}
 
class Bird extends Animal{
   public void move(){
      System.out.println("鳥可以飛");
   }
}
class Dog extends Animal{
  public void move(){
    System.out.println("狗可以跑")
  }
}
 
public class TestMain{
   public static void main(String args[]){
      Animal a = new Animal(); // Animal 對(duì)象
      Animal b = new Bird(); //Bird對(duì)象
      Animal c = new Dog(); // Dog 對(duì)象
            
      a.move();// 執(zhí)行 Animal 類的方法
            b.move(); //執(zhí)行Bird類的方法
      c.move();//執(zhí)行 Dog 類的方法
   }
}

上述程序運(yùn)行的結(jié)果

動(dòng)物可以移動(dòng)
鳥可以飛
狗可以跑

在這個(gè)案例中握牧,Animal是一個(gè)屬于動(dòng)物的抽象類容诬,它定義了一個(gè)方法move()。表示動(dòng)物的具有的行為沿腰。

而動(dòng)物只是一個(gè)泛類別览徒,具體到某種動(dòng)物時(shí),行為方式是不同的颂龙,因此定義了BirdDoc习蓬,分別繼承了Animal這個(gè)類纽什,并且重寫了move()方法,分別實(shí)現(xiàn)這兩種動(dòng)物的行為方式友雳。

重寫的好處在于子類可以根據(jù)需要稿湿,定義特定于自己的行為。 也就是說子類能夠根據(jù)需要實(shí)現(xiàn)父類的方法押赊。

在類繼承關(guān)系中饺藤,父類的非抽象方法,子類是不強(qiáng)制要求重寫的流礁。在實(shí)際應(yīng)用中涕俗,如果重寫了父類的方法,并且實(shí)例對(duì)象的引用指向的是子類時(shí)神帅,JVM會(huì)自動(dòng)調(diào)用子類重寫的方法再姑,此時(shí),父類的方法完全被屏蔽了找御。就像前面測(cè)試的代碼元镀。

父類引用指向子類實(shí)現(xiàn)Dog(),此時(shí)調(diào)用c.move()方法霎桅,只會(huì)調(diào)用到Dog類中的move()方法栖疑。如果Dog子類沒有重寫move()方法,則會(huì)調(diào)用父類Animalmove()方法滔驶。

Animal c = new Dog(); // Dog 對(duì)象

c.move();//執(zhí)行 Dog 類的方法

在有些情況下遇革,子類重寫了父類的方法,我們希望在調(diào)用子類重寫方法的同時(shí)揭糕,仍然能夠調(diào)用到父類被重寫的方法萝快,怎么實(shí)現(xiàn)?

Super關(guān)鍵字

當(dāng)需要在子類中調(diào)用父類的被重寫方法時(shí)著角,要使用 super 關(guān)鍵字揪漩。

class Animal{
   public void move(){
      System.out.println("動(dòng)物可以移動(dòng)");
   }
}
 
class Bird extends Animal{
   public void move(){
      super.move(); //增加super調(diào)用
      System.out.println("鳥可以飛");
   }
}

}
 
public class TestMain{
   public static void main(String args[]){
      Animal b = new Bird(); //Bird對(duì)象  
      b.move();//執(zhí)行 Bird 類的方法
   }
}


運(yùn)行結(jié)果如下:

動(dòng)物可以移動(dòng)
鳥可以飛

方法的重寫規(guī)則

總結(jié)一下,在Java中吏口,方法重寫的規(guī)則奄容。

  • 參數(shù)列表與被重寫方法的參數(shù)列表必須完全相同。
  • 返回類型與被重寫方法的返回類型可以不相同锨侯,但是必須是父類返回值的派生類(java5 及更早版本返回類型要一樣嫩海,java7 及更高版本可以不同)冬殃。
  • 訪問權(quán)限不能比父類中被重寫的方法的訪問權(quán)限更低囚痴。例如:如果父類的一個(gè)方法被聲明為 public,那么在子類中重寫該方法就不能聲明為 protected审葬。
  • 父類的成員方法只能被它的子類重寫深滚。
  • 聲明為 final 的方法不能被重寫奕谭。
  • 聲明為 static 的方法不能被重寫,但是能夠被再次聲明痴荐。
  • 子類和父類在同一個(gè)包中血柳,那么子類可以重寫父類所有方法,除了聲明為 private 和 final 的方法生兆。
  • 子類和父類不在同一個(gè)包中难捌,那么子類只能夠重寫父類的聲明為 public 和 protected 的非 final 方法。
  • 重寫的方法能夠拋出任何非強(qiáng)制異常鸦难,無論被重寫的方法是否拋出異常根吁。但是,重寫的方法不能拋出新的強(qiáng)制性異常合蔽,或者比被重寫方法聲明的更廣泛的強(qiáng)制性異常击敌,反之則可以。
  • 構(gòu)造方法不能被重寫拴事。
  • 如果不能繼承一個(gè)類沃斤,則不能重寫該類的方法。

基于接口實(shí)現(xiàn)的重寫

基于接口實(shí)現(xiàn)的重寫刃宵,在實(shí)際應(yīng)用中衡瓶,使用非常頻繁,以線程實(shí)現(xiàn)為例组去,如圖所示鞍陨,表示Thread和Runnable的類關(guān)系圖。

image-20211027213311085

Runnable是一個(gè)接口从隆,它定義了線程的執(zhí)行方法诚撵,代碼如下:

@FunctionalInterface
public interface Runnable {
    /**
     * When an object implementing interface <code>Runnable</code> is used
     * to create a thread, starting the thread causes the object's
     * <code>run</code> method to be called in that separately executing
     * thread.
     * <p>
     * The general contract of the method <code>run</code> is that it may
     * take any action whatsoever.
     *
     * @see     java.lang.Thread#run()
     */
    public abstract void run();
}

在實(shí)際應(yīng)用中,我們可以直接繼承這個(gè)接口來聲明一個(gè)線程键闺。

Thread寿烟,是一個(gè)普通的線程類,它實(shí)現(xiàn)了Runnable接口辛燥,并且重寫了Runnable這個(gè)接口的run方法筛武,這里這么設(shè)計(jì)的目的是: 避免Java中一個(gè)類只能實(shí)現(xiàn)一個(gè)接口這一規(guī)則導(dǎo)致,如果一個(gè)類已經(jīng)繼承了其他的接口挎塌,但是又想要去實(shí)現(xiàn)線程時(shí)的問題徘六。

public
class Thread implements Runnable {
    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }
}

由于接口只是用來做規(guī)范設(shè)計(jì),用來描述某個(gè)對(duì)象具有什么行為榴都,但是它并沒有具體的實(shí)現(xiàn)待锈,因此如果需要聲明一個(gè)線程,就需要實(shí)現(xiàn)該接口并且重寫里面的抽象方法(接口中未實(shí)現(xiàn)的方法都是抽象的嘴高,子類必須要重寫)竿音。

Thread類中重寫了Runnable中的run方法和屎,該方法調(diào)用了target.run()。這個(gè)target是真正的線程業(yè)務(wù)實(shí)現(xiàn)春瞬,Thread只是一個(gè)委派設(shè)計(jì)模式柴信。

因此,如果我們想通過繼承Thread來實(shí)現(xiàn)線程宽气,則需要按照如下代碼的寫法來實(shí)現(xiàn)随常,其中target就是代表著子類的App這個(gè)對(duì)象實(shí)例。

public class App extends Thread{
    @Override
    public void run() {
        //doSomething
    }
}

由于接口只是一種行為規(guī)范萄涯,本身不提供實(shí)現(xiàn)线罕,因此實(shí)現(xiàn)接口的子類,都“必須”要重寫父類的方法窃判,這個(gè)和類繼承是有區(qū)別的钞楼。

重載

重載(overloading) 是在一個(gè)類里面,方法名字相同袄琳,而參數(shù)不同询件。返回類型可以相同也可以不同。

每個(gè)重載的方法(或者構(gòu)造函數(shù))都必須有一個(gè)獨(dú)一無二的參數(shù)類型列表唆樊。

最常用的地方就是構(gòu)造器的重載宛琅,比如在ThreadPoolExecutor線程池的實(shí)現(xiàn)類中,可看到如下的重載方法逗旁。

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {}
public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory) {}
 public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {}

方法重載的好處就是讓類以統(tǒng)一的方式處理不同類型的一種手段嘿辟,調(diào)用方法時(shí)通過傳遞給他們的不同個(gè)數(shù)和類型的參數(shù)來決定具體使用哪個(gè)方法,這就是多態(tài)性片效。

它的特點(diǎn)是:重載發(fā)生在本類红伦,方法名相同,參數(shù)列表不同淀衣,與返回值無關(guān)昙读,只和方法名,參數(shù)的類型相關(guān)膨桥。

方法重載時(shí)蛮浑,方法之間需要存在一定的聯(lián)系,因?yàn)檫@樣可以提高程序的可讀性只嚣,并且我們一般只重載功能相似的方法沮稚。

重載規(guī)則

  • 被重載的方法必須改變參數(shù)列表(參數(shù)個(gè)數(shù)或類型不一樣);
  • 被重載的方法可以改變返回類型册舞;
  • 被重載的方法可以改變?cè)L問修飾符蕴掏;
  • 被重載的方法可以聲明新的或更廣的檢查異常;
  • 方法能夠在同一個(gè)類中或者在一個(gè)子類中被重載。
  • 無法以返回值類型作為重載函數(shù)的區(qū)分標(biāo)準(zhǔn)囚似。

問題解答

理解了上述知識(shí)點(diǎn)以后,再來看這道面試題线得。

面試題: 重載(Overload)和重寫(Override)的區(qū)別饶唤。重載的方法能否根據(jù)返回類型進(jìn)行區(qū)分

區(qū)別:

  1. 方法重載是一個(gè)類中定義了多個(gè)方法名相同,而他們的參數(shù)的數(shù)量不同或數(shù)量相同而類型和次序不同,則稱為方法的重載(Overloading)。
  2. 方法重寫是在子類存在方法與父類的方法的名字相同,而且參數(shù)的個(gè)數(shù)與類型一樣,返回值也一樣的方法,就稱為重寫(Overriding)贯钩。
  3. 方法重載是一個(gè)類的多態(tài)性表現(xiàn),而方法重寫是子類與父類的一種多態(tài)性表現(xiàn)募狂。

重載方法是否能夠根據(jù)返回類型進(jìn)行區(qū)分

重載方法無法根據(jù)類型來區(qū)分, 它只能通過參數(shù)類型角雷、參數(shù)個(gè)數(shù)來區(qū)分祸穷,但是對(duì)于重載的方法,是允許修改返回值類型勺三、異常類型雷滚、訪問等級(jí),但是不能只根據(jù)這些類型類做重載吗坚。

為什們不能僅根據(jù)返回類型來區(qū)分重載呢祈远?

原因是,在調(diào)用目標(biāo)方法時(shí)商源,是無法指定返回值類型信息的车份,這個(gè)時(shí)候編譯器并不知道你要調(diào)用哪個(gè)函數(shù)。

比如在下面這段代碼中牡彻,當(dāng)調(diào)用max(1,2);時(shí)無法確定調(diào)用的是哪個(gè)扫沼,單從這一點(diǎn)上來說,僅返回值類型不同的重載是不應(yīng)該允許的庄吼。

float max(int a, int b);
int max(int a, int b);

可能有同學(xué)會(huì)問缎除,如果讓編譯器能夠根據(jù)上下文語境來判斷呢?比如像下面這段代碼总寻。

float x=max(1,2);
int y=max(2,3);

在實(shí)際開發(fā)中伴找,很多時(shí)候會(huì)存在這樣一種方法調(diào)用max(1,2),并不會(huì)去聲明返回值废菱,由于這種情況的存在技矮,所以這個(gè)理論也不能實(shí)現(xiàn)。

函數(shù)的返回值只是作為函數(shù)運(yùn)行之后的一個(gè)“狀態(tài)”他是保持方法的調(diào)用者與被調(diào)用者進(jìn)行通信的關(guān)鍵殊轴。并不能作為某個(gè)方法的“標(biāo)識(shí)”

問題總結(jié)

這個(gè)問題衰倦,其實(shí)是屬于那種,你不問我旁理,我一定會(huì)認(rèn)為自己知道樊零,而且在工作開發(fā)中也能使用不會(huì)出問題,但是你一問我,我一定會(huì)懵逼驻襟,不是因?yàn)檎娴牟欢峒瑁遣恢涝趺慈ソM織語言來描述這兩個(gè)概念。

建議大家參考“費(fèi)曼學(xué)習(xí)法”沉衣,就是把這篇文章學(xué)到的理論郁副,通過演講的方式表達(dá)出來,可以和同事豌习,或者自己自問自答存谎。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市肥隆,隨后出現(xiàn)的幾起案子既荚,更是在濱河造成了極大的恐慌,老刑警劉巖栋艳,帶你破解...
    沈念sama閱讀 212,080評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件恰聘,死亡現(xiàn)場離奇詭異,居然都是意外死亡吸占,警方通過查閱死者的電腦和手機(jī)憨琳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,422評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來旬昭,“玉大人篙螟,你說我怎么就攤上這事∥示校” “怎么了遍略?”我有些...
    開封第一講書人閱讀 157,630評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長骤坐。 經(jīng)常有香客問我绪杏,道長,這世上最難降的妖魔是什么纽绍? 我笑而不...
    開封第一講書人閱讀 56,554評(píng)論 1 284
  • 正文 為了忘掉前任蕾久,我火速辦了婚禮,結(jié)果婚禮上拌夏,老公的妹妹穿的比我還像新娘僧著。我一直安慰自己,他們只是感情好障簿,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,662評(píng)論 6 386
  • 文/花漫 我一把揭開白布盹愚。 她就那樣靜靜地躺著,像睡著了一般站故。 火紅的嫁衣襯著肌膚如雪皆怕。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,856評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音愈腾,去河邊找鬼憋活。 笑死,一個(gè)胖子當(dāng)著我的面吹牛虱黄,可吹牛的內(nèi)容都是我干的悦即。 我是一名探鬼主播,決...
    沈念sama閱讀 39,014評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼礁鲁,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了赁豆?” 一聲冷哼從身側(cè)響起仅醇,我...
    開封第一講書人閱讀 37,752評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎魔种,沒想到半個(gè)月后析二,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,212評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡节预,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,541評(píng)論 2 327
  • 正文 我和宋清朗相戀三年叶摄,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片安拟。...
    茶點(diǎn)故事閱讀 38,687評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蛤吓,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出糠赦,到底是詐尸還是另有隱情会傲,我是刑警寧澤,帶...
    沈念sama閱讀 34,347評(píng)論 4 331
  • 正文 年R本政府宣布拙泽,位于F島的核電站淌山,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏顾瞻。R本人自食惡果不足惜泼疑,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,973評(píng)論 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望荷荤。 院中可真熱鬧退渗,春花似錦、人聲如沸蕴纳。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,777評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽袱蚓。三九已至钞啸,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背体斩。 一陣腳步聲響...
    開封第一講書人閱讀 32,006評(píng)論 1 266
  • 我被黑心中介騙來泰國打工梭稚, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人絮吵。 一個(gè)月前我還...
    沈念sama閱讀 46,406評(píng)論 2 360
  • 正文 我出身青樓弧烤,卻偏偏與公主長得像,于是被迫代替她去往敵國和親蹬敲。 傳聞我的和親對(duì)象是個(gè)殘疾皇子暇昂,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,576評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容