一文帶你搞懂Java中方法重寫與方法重載的區(qū)別
一. 方法重寫
在面向?qū)ο笾?,?shí)現(xiàn)多態(tài)的必備條件是繼承、重寫和向上轉(zhuǎn)型,現(xiàn)在我們已經(jīng)學(xué)習(xí)了什么是繼承。接下來我們?cè)賮韺W(xué)習(xí)什么是方法重寫,這是我們能夠?qū)崿F(xiàn)多態(tài)的前提。
1. 概念
如果我們在子類中,創(chuàng)建了一個(gè)與父類中名稱、返回值類型、參數(shù)列表都完全相同的方法,只是方法體的功能實(shí)現(xiàn)不同,這種方式被稱為方法重寫(override) ,或者叫方法覆蓋。當(dāng)父類中的方法無法滿足子類的需求,或者子類需要有特殊功能時(shí),就可以進(jìn)行方法重寫。
2. 基本要求
我們?cè)谶M(jìn)行方法重寫時(shí),需要遵循以下幾點(diǎn)要求:
- 父類的成員方法只能被它的子類重寫,即不能繼承一個(gè)方法,就不能重寫這個(gè)方法;
- 被final修飾的方法不能被重寫;
- 被static修飾的方法不能被重寫,但可以再次聲明;
- 構(gòu)造方法不能被重寫;
- 子類和父類在同一個(gè)包中時(shí),子類可以重寫父類中除了被private和final修飾的其他所有方法;
- 子類和父類不在同一個(gè)包中時(shí),子類只能重寫父類被public和protected修飾的非final方法;
- 重寫的方法建議使用@Override注解來標(biāo)識(shí)。
3. 注意事項(xiàng)
另外我們?cè)谶M(jìn)行方法重寫時(shí),還要注意以下幾點(diǎn):
- 方法簽名要相同:重寫的方法和被重寫的方法,在方法名、參數(shù)上都要相同;
- 返回值類型一致:JDK 1.5之前重寫方法的返回值類型必須一樣,但之后的Java版本放寬了限制,返回值類型必須小于或等于父類方法的返回值類型;
- 訪問修飾符要更寬泛:子類重寫父類的方法時(shí),子類方法中的訪問修飾符不能比父類中的更嚴(yán)格(public>protected>default>private)。比如父類方法的修飾符是protected,則子類的同名方法其修飾符可以是protected或public,但不能是默認(rèn)的或private;
- 聲明的異常類型要一致:重寫的方法一定不能拋出新的檢査異常,或者比被重寫方法聲明更寬泛的檢査型異常。例如,父類的方法聲明了IOException,重寫該方法時(shí)就不能拋出Exception,只能拋出IOException或其子類異常。但可以拋出非檢査異常。
4. 代碼實(shí)現(xiàn)
接下來就通過一個(gè)案例來給大家講解方法的重寫該怎么實(shí)現(xiàn)。
4.1 定義父類
我們先定義一個(gè)Father父類,要注意父類中有哪些方法不能被重寫。
public class Father { // 父類中的成員變量--變量隱藏 String name="老子"; //構(gòu)造方法不能被重寫,因?yàn)闃?gòu)造方法不能被繼承! public Father() { System.out.println("爹的構(gòu)造方法"); } // 吃 public void eat() { System.out.println("爹吃饅頭"); } // 喝 public void drink() { System.out.println("爹喝水"); } // 玩 //私有方法不能被重寫 // private void play() { // System.out.println("爹玩火"); // } //靜態(tài)方法不能被重寫,但可以在子類中聲明一個(gè)同樣的靜態(tài)方法。 // public static void play() { // System.out.println("爹玩火"); // } //final方法不能被重寫 public final void play() { System.out.println("爹玩火"); } }
4.2 定義子類
定義一個(gè)Son子類繼承父類,有了繼承才會(huì)有重寫!
public class Son extends Father{ //構(gòu)造方法不能被重寫,因?yàn)闃?gòu)造方法不能被繼承! //@Override //public Father() {} // 吃 /** * 方法重寫時(shí)可以帶有@Ovriride關(guān)鍵詞,當(dāng)重寫的方法簽名不一致時(shí),可以有編譯錯(cuò)誤的提示。 * 否則方法簽名不一致時(shí)不會(huì)有錯(cuò)誤提示,會(huì)被當(dāng)做一個(gè)新的方法來處理。 */ @Override public void eat() { //如果子類的功能,是在父類的基礎(chǔ)之上進(jìn)行的額外擴(kuò)展增加, //我們可以使用super關(guān)鍵字調(diào)用父類的同名方法,然后再進(jìn)行自己的額外擴(kuò)展! //如果子類的實(shí)現(xiàn)和父類完全不一樣,可以不調(diào)用super! super.eat(); //方法重寫時(shí),子類可以對(duì)父類的同名方法進(jìn)行擴(kuò)展實(shí)現(xiàn),方法體的內(nèi)容可以和父類中的實(shí)現(xiàn)不一樣 System.out.println("兒子吃肉"); } // 喝 @Override public void drink() { //如果子類的實(shí)現(xiàn)和父類完全不一樣,可以不調(diào)用super! System.out.println("兒子喝酒"); } /** * 父類私有的、static、final等方法無法被重寫 */ //@Override //public void play() {} //static靜態(tài)的父類方法不能被重寫,但可以在子類中再重新編寫一個(gè)靜態(tài)的同名方法。 //public static void play() {} //變量隱藏--調(diào)用父類和子類中的同名成員變量 public void sayHello() { // 如果子類的實(shí)現(xiàn)和父類完全不一樣,可以不調(diào)用super! System.out.println("父親的名字=" + super.name); System.out.println("兒子的名字=" + name); } public static void main(String[] args) { Son son = new Son(); son.sayHello(); } }
我們?cè)谶M(jìn)行方法重寫時(shí),要注意以下幾點(diǎn):
- 方法重寫時(shí)可以帶有@Ovriride關(guān)鍵詞。當(dāng)重寫的方法簽名不一致時(shí),會(huì)有編譯錯(cuò)誤的提示,否則方法簽名不一致時(shí)不會(huì)有錯(cuò)誤提示,會(huì)被當(dāng)做一個(gè)新的方法來處理。
- 當(dāng)子類對(duì)象調(diào)用重寫的方法時(shí),默認(rèn)執(zhí)行的是子類的方法,而不是父類中被重寫的方法。如果我們想要調(diào)用父類中被重寫的方法,則可以使用“super.方法名”的形式。
- 如果子類的功能是在父類的基礎(chǔ)之上進(jìn)行的額外擴(kuò)展,我們可以使用super關(guān)鍵字調(diào)用父類的同名方法,然后再進(jìn)行自己的額外擴(kuò)展!
- 如果子類的實(shí)現(xiàn)和父類完全不一樣,可以不調(diào)用super!
- 方法重寫時(shí),子類可以對(duì)父類的同名方法進(jìn)行擴(kuò)展實(shí)現(xiàn),方法體的內(nèi)容可以和父類中的實(shí)現(xiàn)不一樣。
4.3 @Override注解
在上面的代碼中,我們用到了一個(gè)新的關(guān)鍵字@Override。在Java中,@Override是一個(gè)注解,關(guān)于注解的更多內(nèi)容,會(huì)在后面的文章中進(jìn)行專門講解,現(xiàn)在我們先知道注解這個(gè)概念就行。
@Override是一個(gè)用來修飾被重新的方法的注解,只能用在被重新的方法上,不能用在其它的地方。該注解可以強(qiáng)制子類必須重寫父類的方法或者接口中的方法,主要是告訴編譯器檢查重寫的方法是否和父類中定義的一致。如果重寫的方法簽名不一致,會(huì)提示編譯錯(cuò)誤。如果方法簽名不一致,則不會(huì)有錯(cuò)誤提示,會(huì)被當(dāng)做一個(gè)新的方法來處理。通過這樣的機(jī)制,就可以避免程序員出現(xiàn)一些低級(jí)的錯(cuò)誤。
5. 變量隱藏
5.1 概念
如果子類中定義了一個(gè)成員變量,而該變量的名稱與父類中的成員變量相同,數(shù)據(jù)類型不一定完全一致,我們就把這稱為變量隱藏。也就是說,子類的成員變量,對(duì)從父類繼承過來的成員變量進(jìn)行了重新定義,出現(xiàn)了子類變量對(duì)父類變量的隱藏。所以子類執(zhí)行自己定義的方法時(shí),操作的成員變量默認(rèn)是自己定義的變量,而不是父類中的同名變量。如果我們非要操作隱藏的成員變量,可以使用super關(guān)鍵字進(jìn)行調(diào)用。
接下來我們通過一個(gè)案例來給大家演示變量隱藏的使用。
5.2 案例實(shí)現(xiàn)
父類中定義一個(gè)成員變量name,如下圖所示:
子類中也定義一個(gè)相同的成員變量name,如下圖所示:
如果我們?cè)赟on類中直接使用name,默認(rèn)使用的是Son自己的變量;如果我們想使用Father類中的name變量,則可以通過“super.屬性”的形式進(jìn)行。執(zhí)行結(jié)果如下圖所示:
6. 方法隱藏
在子類繼承父類時(shí),既然存在變量隱藏的現(xiàn)象,同理也存在方法隱藏的現(xiàn)象。
6.1 概念
我們知道,方法的重寫是子類覆蓋父類的對(duì)象方法,而方法隱藏則是子類覆蓋父類的 靜態(tài)方法(類方法) 。在java中的靜態(tài)方法能被子類繼承嗎?答案是肯定的,但若子類中有與父類中同名同參的方法,則父類的方法將被隱藏。
6.2 案例實(shí)現(xiàn)
我們先定義一個(gè)Father父類,里面有個(gè)靜態(tài)方法eat。
/** * @author 一一哥Sun * * 定義父類 */ public class Father { // 吃---靜態(tài)方法 public static void eat() { System.out.println("爹吃饅頭"); } }
然后再定義一個(gè)Son子類,里面也有一個(gè)靜態(tài)方法eat。我們知道,靜態(tài)方法是可以被繼承的,所以如果Son子類中沒有定義自己的eat()方法,默認(rèn)可以使用Father父類中的eat()方法。但如果我們?cè)谧宇愔幸捕x了一個(gè)eat()方法,子類的同名靜態(tài)方法就會(huì)隱藏父類中的eat()方法,這就是方法隱藏。
/** * @author 一一哥Sun * * 子類繼承父類 */ public class Son extends Father { // 吃---靜態(tài)方法 //如果子類中沒有定義該方法,則子類可以繼承使用父類的eat()方法 public static void eat() { //子類覆蓋父類中的同名靜態(tài)方法(類) System.out.println("兒子吃肉"); } public static void main(String[] args) { //調(diào)用子類自己的靜態(tài)方法 eat(); //調(diào)用父類的靜態(tài)方法 Father.eat(); } }
執(zhí)行結(jié)果如下圖所示:
6.3 小結(jié)
通過本案例,我們可以得出以下結(jié)論:
子類可以繼承父類中的靜態(tài)方法;
子類無法重寫父類中的靜態(tài)方法,但可以重載;
若子類中定義了與父類中同樣的靜態(tài)方法,則父類的同名方法會(huì)被子類隱藏。
二. 重寫與重載的區(qū)別
在Java中既有方法重寫(Override),也有方法重載(Overload),對(duì)于初學(xué)者來說很容易搞混。所以有不少面試官,在招聘初級(jí)程序員時(shí),就很喜歡問我們方法重寫與方法重載的區(qū)別。
其實(shí)方法重寫Override和方法重載Overload的最大不同,在于方法簽名的不同。如果同一個(gè)類中的多個(gè)方法簽名不同,就是方法重載Overload,重載出的方法是一個(gè)新方法。如果父子類之間的多個(gè)方法簽名相同,且返回值也相同,就是方法重寫Override。
當(dāng)然,如果你想把關(guān)于重寫和重載的區(qū)別說得更詳細(xì),可以參考以下章節(jié)。
1. 重載的特點(diǎn)
- 方法重載要求方法同名不同參(參數(shù)類型、個(gè)數(shù)、順序);
- 重載的方法與返回值、訪問修飾符無關(guān);
- 重載的方法發(fā)生在同一個(gè)類中,是在一個(gè)類中創(chuàng)建多個(gè)同名的方法。
2. 重寫的特點(diǎn)
- 重寫的方法發(fā)生在父子類中,需要有繼承關(guān)系;
- 父類的成員方法只能被它的子類重寫,即不能繼承一個(gè)方法,就不能重寫這個(gè)方法;
- 被final修飾的方法不能被重寫;
- 被static修飾的方法不能被重寫,但可以再次聲明;
- 構(gòu)造方法不能被重寫;
- 子類和父類在同一個(gè)包中時(shí),子類可以重寫父類中除了被private和final修飾的其他所有方法;
- 子類和父類不在同一個(gè)包中時(shí),子類只能重寫父類被public和protected修飾的非final方法;
- 方法重寫時(shí)可以使用@Override注解;
- 方法簽名要相同;
- 返回值類型一致;
- 訪問修飾符要更寬泛;
- 聲明的異常類型要一致。
三. 結(jié)語
現(xiàn)在你知道方法重寫是怎么回事了嗎?另外方法重載和方法重寫的區(qū)別,是我們面試初級(jí)程序員時(shí)很常見的題目,大家一定要牢牢掌握哦?,F(xiàn)在有了方法重寫的基礎(chǔ),接下來我們就可以學(xué)習(xí)多態(tài)的內(nèi)容了,敬請(qǐng)關(guān)注下一篇文章哦。
以上就是一文帶你搞懂Java中方法重寫與方法重載的區(qū)別的詳細(xì)內(nèi)容,更多關(guān)于Java 方法重寫與方法重載的區(qū)別的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
java實(shí)現(xiàn)微信公眾平臺(tái)發(fā)送模板消息的示例代碼
這篇文章主要介紹了java實(shí)現(xiàn)微信公眾平臺(tái)發(fā)送模板消息的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09JPA @GeneratedValue 四種標(biāo)準(zhǔn)用法TABLE,SEQUENCE,IDENTITY,
這篇文章主要介紹了@GeneratedValue 四種標(biāo)準(zhǔn)用法TABLE,SEQUENCE,IDENTITY,AUTO詳解,需要的朋友可以參考下2024-03-03Java ConcurrentModificationException異常解決案例詳解
這篇文章主要介紹了Java ConcurrentModificationException異常解決案例詳解,本篇文章通過簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-09-09java程序代碼與文本對(duì)比實(shí)用工具簡(jiǎn)介
可以對(duì)兩段文本進(jìn)行對(duì)比,檢測(cè)/比較兩個(gè)文本有什么不同的差異,以便修改,常用于程序代碼,就是不需要人工查看,尤其是大文件,有幾百上千行的代碼,這時(shí)候就建議使用比較工具了,不用浪費(fèi)過多時(shí)間去尋找2021-09-09SpringCloud修改Feign日志記錄級(jí)別過程淺析
OpenFeign源于Netflix的Feign,是http通信的客戶端。屏蔽了網(wǎng)絡(luò)通信的細(xì)節(jié),直接面向接口的方式開發(fā),讓開發(fā)者感知不到網(wǎng)絡(luò)通信細(xì)節(jié)。所有遠(yuǎn)程調(diào)用,都像調(diào)用本地方法一樣完成2023-02-02SpringSecurity 自定義認(rèn)證登錄的項(xiàng)目實(shí)踐
本文主要介紹了SpringSecurity 自定義認(rèn)證登錄的項(xiàng)目實(shí)踐,以手機(jī)驗(yàn)證碼登錄為例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-08-08mybatisplus?實(shí)現(xiàn)接口MetaObjectHandler自動(dòng)填充字段值
MetaObjectHandler是MyBatis-Plus提供的一個(gè)接口,本文主要介紹了mybatisplus?實(shí)現(xiàn)接口MetaObjectHandler自動(dòng)填充字段值,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-07-07