天天看點

【Java】重載和重寫

前言

在程式設計中經常會遇到要對方法進行重載或者重寫的情況,下面就介紹一下重載和重寫。

重載(Overloade)

重載出現的原因

任何程式設計語言都具備的一項重要特性就是對名字的運用。方法名就是給某個動作取的名字。通過使用名字,我們可以引用所有對象和方法。名字起的好可以使程式更易于了解和修改。

在大多數程式設計語言中要求為每個方法提供唯一的辨別符。不能使用print()的方法顯示了整數之後,又用一個名為print()的方法顯示浮點數。即每個方法都要有唯一的名稱。這是迫使出現

重載方法

的理由之一。若是print()函數可以被重載了,那麼就既可以輸出整數也可以輸出浮點數。又例如,我們要計算兩個整數相加,我們可以設計方法為int add(int a, int b)。調用add(10,10)我們就可以知道是計算兩個整數相加。此時,我們又想計算兩個浮點數相加,因為add見名知意是以我們繼續使用這個名字,那麼就需要方法double add(double a, double c)。于是,add()方法就被

重載

了。

在Java(和C++)裡,構造器是強制重載方法出現的另一個原因。構造器的名字由類名決定,那麼就隻能有一個構造器。但是,又想使用多種方式建立對象又該怎麼辦呢?那麼就隻有重載構造器,使得同名不同參的構造器同時存在。

重載的規則

方法重載是在一個類裡面,方法名相同,而參數不同,對傳回值沒有強制要求可以相同也可以不同。方法重載需要注意一下幾點:

  • 被重載的方法必須改變參數清單(參數個數或者類型不一樣)
  • 被重載的方法不介意改變傳回類型和通路修飾符
  • 被重載的方法可以聲明新的或者更廣的檢查異常
  • 方法能夠在同一個類中或者在一個子類中被重載
  • 不可以傳回值類型作為分區重載函數的标準

區分重載方法

每個重載的方法都有獨一無二的參數類型清單。是以在區分重載方法時,隻能以類名和方法的形參清單作為标準。

那為什麼不能使用方法的傳回值來區分呢?

比如下面兩個方法,雖然它們有相同的名字和形式參數,但是卻很容易區分它們

void f(){}
int f() {return 1;}
           

隻要編譯器可以根據語境明确判斷出語義,比如在int x = f()中,那麼的确可以根據此區分方法。不過,有時你并不關心方法的傳回值,你想要的是方法調用的其他效果,這時你可能會調用方法而忽略傳回值。是以,向下面這樣的調用方法:

f();
           

此時Java該怎樣判斷調用的是哪個方法呢?别人也無法了解這個代碼的含義。是以,依據方法的傳回值來區分重載方法是行不通的。

重寫(Override)

重寫出現的原因

先看一個例子

class Animal{
    public void printWhoIAm(){System.out.println("Animal")}
}
public class Dog extends Animal{
    public static void main(String args[]){
        Dog dog = new Dog();
        dog.printWhoIAm();
    }
} 
/*
output:
Animal
*/
           

當dog調用printWhoIAm()方法時,其實希望的是輸出“dog”,而不是Animal。要實作輸出“Dog”,想到了重載,可是重載要求被重載的方法具有不同的形參清單。這個方法不得行。dog調用的printWhoIAm()是父類中的,在子類中若是可以重寫這個方法,那麼就可以實作目的了。于是,重寫(覆寫)便産生了,為了解決父類方法在子類中不适用,而讓子類重寫方法的方式。

我們解決以上代碼需求如下:

class Animal{
    public void printWhoIAm(){
        System.out.println("Animal")
    }
}
public class Dog extends Animal{
    //加上注解@Override可以強制進行重寫的檢查 防止自己重寫錯誤
    @Override
    public void printWhoIAm(){
        System.out.println("Dog")
    }
    
    public static void main(String args[]){
        Dog dog = new Dog();
        dog.printWhoIAm();
    }
} 
/*
output:
Dog
*/
           

重寫的規則

  • 重寫是對父類允許通路的方法的實作過程進行重新編寫,傳回值不變或者為子類、形參清單不能改變并且通路控制權限不能嚴于父類。父類為default包通路權限,則子類就為public或者default;若父類是public,則子類必須為public。
  • 子類可以重寫父類的除了構造器的任何方法。構造器是和類名相同的,不能被子類繼承,是以也不可以被重寫。
  • 重寫方法不能抛出新的檢查異常或者比被重寫方法申明更加寬泛的異常。例如: 父類的一個方法申明了一個檢查異常 IOException,但是在重寫這個方法的時候不能抛出 Exception 異常,因為 Exception 是 IOException 的父類,隻能抛出 IOException 的子類異常。

子類可以重寫父類中通路控制權限為private的方法嗎?

答案是不可以。父類中的private方法對子類來說是不可見的,就算子類中完全按照重寫要求定義方法,也不能算重寫父類中的方法,實際上隻是子類新增的一個方法。是以,隻有非private方法才可以被重寫。

super.方法()和this.方法()的差別

子類若是重寫了父類的方法,那麼父類原來的這個方法還可以被調用嗎?

答案是可以的,使用super對父類的方法進行調用。

class Animal{
    public void printWhoIAm(){System.out.println("Animal")}
}
public class Dog extends Animal{
    //加上注解@Override可以強制進行重寫的檢查 防止自己重寫錯誤
    @Override
    public void printWhoIAm(){System.out.println("Dog")}
    public void print(){
        super.printWhoIAm();
        printWhoIAm();// this.printWhoIAm();
    }
    public static void main(String args[]){
        Dog dog = new Dog();
        dog.print();
    }
} 
/*
output:
Animal
Dog
*/
           

使用

this.方法()

會先在本類中查找是否存在要調用的方法,如果不存在則查找父類中是否具備此方法。如果有則調用,否則出現編譯時錯誤。

super.方法()

會明确表示調用父類中的方法,直接去父類尋找要調用的方法。

重載和重寫的差別

差別 重載 重寫
參數清單 必須改 一定不能改
傳回類型 可以修改
通路控制權限 不能比父類嚴格
異常 可以減少或删除,一定不能抛出新的或者更廣的異常
發生範圍 可以在一個類中也可以在子類中 在子類中

小結

需要注意的是重載和重寫的定義形式。引用菜鳥教程的兩句話和一張圖結束。

  • 方法重載是一個類中定義了多個方法名相同,而他們的參數的數量不同或數量相同而類型和次序不同,則稱為方法的重載(Overloading)。
  • 方法重寫是在子類存在方法與父類的方法的名字相同,而且參數的個數與類型一樣,傳回值也一樣的方法,就稱為重寫(Overriding)

參考:

[1] Eckel B. Java程式設計思想(第四版)[M]. 北京: 機械工業出版社, 2007

[2] 菜鳥教程. Java 重寫(Override)與重載(Overload) |[EB/OL]. /2019-02-18. https://www.runoob.com/java/java-override-overload.html.

每天進步一點點,不要停止前進的腳步~