首頁>技術>

哎呀,等老久了吧!關於 Java 方面的文章終於來了,快快快,扶寡人起來,還能再舉——鼎(明眼人都能看的出來,我受大秦帝國之縱橫天下這部劇的影響了)。

說回正題。重寫(Overriding)算是 Java 中一個非常重要的概念,理解重寫到底是什麼對每個 Java 程式設計師來說都至關重要,這篇文章就來給大家說說重寫過程中應當遵守的 11 條規則。

01、什麼是重寫?

重寫帶來了一種非常重要的能力,可以讓子類重新實現從父類那繼承過來的方法。在下面這幅圖中,Animal 是父類,Dog 是子類,Dog 重新實現了 move() 方法用來和父類進行區分,畢竟狗狗跑起來還是比較有特色的。

重寫的方法和被重寫的方法,不僅方法名相同,引數也相同(也就說是方法簽名相同),只不過,方法體有所不同。

02、哪些方法可以被重寫?

規則一:只能重寫繼承過來的方法

因為重寫是在子類重新實現從父類繼承過來的方法時發生的,所以只能重寫繼承過來的方法,這很好理解。這就意味著,只能重寫那些被 public、protected 或者 default 修飾的方法,private 修飾的方法無法被重寫。

Animal 類有 move()、eat() 和 sleep() 三個方法:

public class Animal {    public void move() { }    protected void eat() { }        void sleep(){ }}

Dog 類來重寫這三個方法:

public class Dog extends Animal {    public void move() { }    protected void eat() { }    void sleep(){ }}

OK,完全沒有問題。但如果父類中的方法是 private 的,就行不通了。

public class Animal {    private void move() { }}

此時,Dog 類中的 move() 方法就不再是一個重寫方法了,因為父類的 move() 方法是 private 的,對子類並不可見。

public class Dog extends Animal {    public void move() { }}
03、哪些方法不能被重寫?

規則二:final、static 的方法不能被重寫

一個方法是 final 的就意味著它無法被子類繼承到,所以就沒辦法重寫。

public class Animal {    final void move() { }}

由於父類 Animal 中的 move() 是 final 的,所以子類在嘗試重寫該方法的時候就出現編譯錯誤了!

同樣的,如果一個方法是 static 的,也不允許重寫,因為靜態方法可用於父類以及子類的所有例項。

public class Animal {    final void move() { }}

重寫的目的在於根據物件的型別不同而表現出多型,而靜態方法不需要建立物件就可以使用。沒有了物件,重寫所需要的“物件的型別”也就沒有存在的意義了。

04、重寫方法的要求

規則三:重寫的方法必須有相同的引數列表

public class Animal {    void eat(String food) { }}

Dog 類中的 eat() 方法保持了父類方法 eat() 的同一個調調,都有一個引數——String 型別的 food。

public class Dog extends Animal {    public void eat(String food) { }}

一旦子類沒有按照這個規則來,比如說增加了一個引數:

public class Dog extends Animal {    public void eat(String food, int amount) { }}

這就不再是重寫的範疇了,當然也不是過載的範疇,因為過載考慮的是同一個類。

規則四:重寫的方法必須返回相同的型別

父類沒有返回型別:

public class Animal {    void eat(String food) { }}

子類嘗試返回 String:

public class Dog extends Animal {    public String eat(String food) {        return null;    }}

於是就編譯出錯了(返回型別不相容)。

規則五:重寫的方法不能使用限制等級更嚴格的許可權修飾符

可以這樣來理解:

如果被重寫的方法是 default,那麼重寫的方法可以是 default、protected 或者 public。如果被重寫的方法是 protected,那麼重寫的方法只能是 protected 或者 public。如果被重寫的方法是 public, 那麼重寫的方法就只能是 public。

舉個例子,父類中的方法是 protected:

public class Animal {    protected void eat() { }}

子類中的方法可以是 public:

public class Dog extends Animal {    public void eat() { }}

如果子類中的方法用了更嚴格的許可權修飾符,編譯器就報錯了。

規則六:重寫後的方法不能丟擲比父類中更高級別的異常

舉例來說,如果父類中的方法丟擲的是 IOException,那麼子類中重寫的方法不能丟擲 Exception,可以是 IOException 的子類或者不丟擲任何異常。這條規則只適用於可檢查的異常。

可檢查(checked)異常必須在原始碼中顯式地進行捕獲處理,不檢查(unchecked)異常就是所謂的執行時異常,比如說 NullPointerException、ArrayIndexOutOfBoundsException 之類的,不會在編譯器強制要求。

父類丟擲 IOException:

public class Animal {    protected void eat() throws IOException { }}

子類丟擲 FileNotFoundException 是可以滿足重寫的規則的,因為 FileNotFoundException 是 IOException 的子類。

public class Dog extends Animal {   public void eat() throws FileNotFoundException { }}

如果子類丟擲了一個新的異常,並且是一個 checked 異常:

public class Dog extends Animal {   public void eat() throws FileNotFoundException, InterruptedException { }}

那編譯器就會提示錯誤:

Error:(9, 16) java: com.itwanger.overriding.Dog中的eat()無法覆蓋com.itwanger.overriding.Animal中的eat()  被覆蓋的方法未丟擲java.lang.InterruptedException

但如果子類丟擲的是一個 unchecked 異常,那就沒有衝突:

public class Dog extends Animal {   public void eat() throws FileNotFoundException, IllegalArgumentException { }}

如果子類丟擲的是一個更高級別的異常:

public class Dog extends Animal {   public void eat() throws Exception { }}

編譯器同樣會提示錯誤,因為 Exception 是 IOException 的父類。

Error:(9, 16) java: com.itwanger.overriding.Dog中的eat()無法覆蓋com.itwanger.overriding.Animal中的eat()  被覆蓋的方法未丟擲java.lang.Exception
05、如何呼叫被重寫的方法?

規則七:可以在子類中透過 super 關鍵字來呼叫父類中被重寫的方法

子類繼承父類的方法而不是重新實現是很常見的一種做法,在這種情況下,可以按照下面的形式呼叫父類的方法:

super.overriddenMethodName();

來看例子。

public class Animal {    protected void eat() { }}

子類重寫了 eat() 方法,然後在子類的 eat() 方法中,可以在方法體的第一行透過 super.eat() 呼叫父類的方法,然後再增加屬於自己的程式碼。

public class Dog extends Animal {   public void eat() {       super.eat();       // Dog-eat   }}
06、重寫和構造方法

規則八:構造方法不能被重寫

因為構造方法很特殊,而且子類的構造方法不能和父類的構造方法同名(類名不同),所以構造方法和重寫之間沒有任何關係。

07、重寫和抽象方法

規則九:如果一個類繼承了抽象類,抽象類中的抽象方法必須在子類中被重寫

先來看這樣一個介面類:

public interface Animal {    void move();}

介面中的方法預設都是抽象方法,透過反編譯是可以看得到的:

public interface Animal{    public abstract void move();}

如果一個抽象類實現了 Animal 介面,move() 方法不是必須被重寫的:

public abstract class AbstractDog implements Animal {    protected abstract void bark();}

但如果一個類繼承了抽象類 AbstractDog,那麼 Animal 介面中的 move() 方法和抽象類 AbstractDog 中的抽象方法 bark() 都必須被重寫:

public class BullDog extends AbstractDog {     public void move() {}     protected void bark() {}}
08、重寫和 synchronized 方法

規則十:synchronized 關鍵字對重寫規則沒有任何影響

synchronized 關鍵字用於在多執行緒環境中獲取和釋放監聽物件,因此它對重寫規則沒有任何影響,這就意味著 synchronized 方法可以去重寫一個非同步方法。

09、重寫和 strictfp 方法

規則十一:strictfp 關鍵字對重寫規則沒有任何影響

如果你想讓浮點運算更加精確,而且不會因為硬體平臺的不同導致執行的結果不一致的話,可以在方法上新增 strictfp 關鍵字。因此 strictfp 關鍵和重寫規則無關。

10
最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • RPA教學——使用 python 寫出詩一樣的程式碼 (三)