看了這篇,我肯定你已經完全搞懂Java的繼承了


遇到認真的讀者是做者的一種幸運,真的,上一篇接口推送後,有好幾個讀者留言說,「二哥,你有一處內容須要修正,應該是接口中不能有 private 和 protected 修飾的方法。」說實話,看到這樣的留言,我心裏是很是欣慰的,由於你投出去的一塊石頭在水面上激起了一串美麗的漣漪。java

在 Java 中,一個類能夠繼承另一個類或者實現多個接口,我想這一點,大部分的讀者應該都知道了。還有一點,我不肯定你們是否知道,就是一個接口也能夠繼承另一個接口,就像下面這樣:git

public interface OneInterface extends Cloneable {
}

這樣作有什麼好處呢?我想有一部分讀者應該已經猜出來了,就是實現了 OneInterface 接口的類,也可使用 Object.clone() 方法了。github

public class TestInterface implements OneInterface {
    public static void main(String[] args) throws CloneNotSupportedException {
        TestInterface c1 = new TestInterface();
        TestInterface c2 = (TestInterface) c1.clone();
    }
}

除此以外,咱們還能夠在 OneInterface 接口中定義其餘一些抽象方法(好比說深拷貝),使該接口擁有 Cloneable 所不具備的功能。ide

public interface OneInterface extends Cloneable {
    void deepClone();
}

看到了吧?這就是繼承的好處:子接口擁有了父接口的方法,使得子接口具備了父接口相同的行爲;同時,子接口還能夠在此基礎上自由發揮,添加屬於本身的行爲post

以上,把「接口」換成「類」,結論一樣成立。讓咱們來定義一個普通的父類 Wanger:測試

public class Wanger {
    int age;
    String name;
    void write() {
        System.out.println("我寫了本《基督山伯爵》");
    }
}

而後,咱們再來定義一個子類 Wangxiaoer,使用關鍵字 extends 來繼承父類 Wanger:this

public class Wangxiaoer extends Wanger{
    @Override
    void write() {
        System.out.println("我寫了本《茶花女》");
    }
}

咱們能夠將通用的方法和成員變量放在父類中,達到代碼複用的目的;而後將特殊的方法和成員變量放在子類中,除此以外,子類還能夠覆蓋父類的方法(好比write() 方法)。這樣,子類也就煥發出了新的生命力。spa

Java 只支持單一繼承,這一點,我在上一篇接口的文章中已經提到過了。若是一個類在定義的時候沒有使用 extends 關鍵字,那麼它隱式地繼承了 java.lang.Object 類——在我看來,這恐怕就是 Java 號稱萬物皆對象的真正緣由了。3d

那究竟子類繼承了父類的什麼呢?code

子類能夠繼承父類的非 private 成員變量,爲了驗證這一點,咱們來看下面這個示例。

public class Wanger {
    String defaultName;
    private String privateName;
    public String publicName;
    protected String protectedName;
}

父類 Wanger 定義了四種類型的成員變量,缺省的 defaultName、私有的 privateName、共有的 publicName、受保護的 protectedName。

在子類 Wangxiaoer 中定義一個測試方法 testVariable()

能夠確認,除了私有的 privateName,其餘三種類型的成員變量均可以繼承到。

同理,子類能夠繼承父類的非 private 方法,爲了驗證這一點,咱們來看下面這個示例。

public class Wanger {
    void write() {
    }

    private void privateWrite() {
    }

    public void publicWrite() {
    }

    protected void protectedWrite() {
    }
}

父類 Wanger 定義了四種類型的方法,缺省的 write、私有的 privateWrite()、共有的 publicWrite()、受保護的 protectedWrite()。

在子類 Wangxiaoer 中定義一個 main 方法,並使用 new 關鍵字新建一個子類對象:

能夠確認,除了私有的 privateWrite(),其餘三種類型的方法均可以繼承到。

不過,子類沒法繼承父類的構造方法。若是父類的構造方法是帶有參數的,代碼以下所示:

public class Wanger {
    int age;
    String name;

    public Wanger(int age, String name) {
        this.age = age;
        this.name = name;
    }
}

則必須在子類的構造器中顯式地經過 super 關鍵字進行調用,不然編譯器將提示如下錯誤:

修復後的代碼以下所示:

public class Wangxiaoer extends Wanger{
    public Wangxiaoer(int age, String name) {
        super(age, name);
    }
}

is-a 是繼承的一個明顯特徵,就是說子類的對象引用類型能夠是一個父類類型。

public class Wangxiaoer extends Wanger{
    public static void main(String[] args) {
        Wanger wangxiaoer = new Wangxiaoer();
    }
}

同理,子接口的實現類的對象引用類型也能夠是一個父接口類型。

public interface OneInterface extends Cloneable {
}
public class TestInterface implements OneInterface {
    public static void main(String[] args) {
        Cloneable c1 = new TestInterface();
    }
}

儘管一個類只能繼承一個類,但一個類卻能夠實現多個接口,這一點,我在上一篇文章也提到過了。另外,還有一點我也提到了,就是 Java 8 以後,接口中能夠定義 default 方法,這很方便,但也帶來了新的問題:

若是一個類實現了多個接口,而這些接口中定義了相同簽名的 default 方法,那麼這個類就要重寫該方法,不然編譯沒法經過。

FlyInterface 是一個會飛的接口,裏面有一個簽名爲 sleep() 的默認方法:

public interface FlyInterface {
    void fly();
    default void sleep() {
        System.out.println("睡着飛");
    }
}

RunInterface 是一個會跑的接口,裏面也有一個簽名爲 sleep() 的默認方法:

public interface RunInterface {
    void run();
    default void sleep() {
        System.out.println("睡着跑");
    }
}

Pig 類實現了 FlyInterface 和 RunInterface 兩個接口,但這時候編譯出錯了。

本來,default 方法就是爲實現該接口而不覆蓋該方法的類提供默認實現的,如今,相同方法簽名的 sleep() 方法把編譯器搞懵逼了,只能重寫了。

public class Pig implements FlyInterfaceRunInterface {

    @Override
    public void fly() {
        System.out.println("會飛的豬");
    }

    @Override
    public void sleep() {
        System.out.println("只能重寫了");
    }

    @Override
    public void run() {
        System.out.println("會跑的豬");
    }
}

類雖然不能繼承多個類,但接口卻能夠繼承多個接口,這一點,我不知道有沒有觸及到一些讀者的知識盲區。

public interface WalkInterface extends FlyInterface,RunInterface{
    void walk();
}

學到了吧?學到就是賺到。

https://github.com/itwanger/JavaBooks

相關文章
相關標籤/搜索