天天看點

看了這篇,我确定你已經徹底搞懂Java的繼承了

看了這篇,我确定你已經徹底搞懂Java的繼承了

遇到認真的讀者是作者的一種幸運,真的,上一篇接口推送後,有好幾個讀者留言說,“二哥,你有一處内容需要修正,應該是接口中不能有 private 和 protected 修飾的方法。”說實話,看到這樣的留言,我内心是非常欣慰的,因為你投出去的一塊石頭在水面上激起了一串美麗的漣漪。

在 Java 中,一個類可以繼承另外一個類或者實作多個接口,我想這一點,大部分的讀者應該都知道了。還有一點,我不确定大家是否知道,就是一個接口也可以繼承另外一個接口,就像下面這樣:

public interface OneInterface extends Cloneable {

}

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

public class TestInterface implements OneInterface {

    public static void main(String[] args) throws CloneNotSupportedException {

        TestInterface c1 = new TestInterface();

        TestInterface c2 = (TestInterface) c1.clone();

    }

除此之外,我們還可以在 OneInterface 接口中定義其他一些抽象方法(比如說深拷貝),使該接口擁有 Cloneable 所不具有的功能。

    void deepClone();

看到了吧?這就是繼承的好處:子接口擁有了父接口的方法,使得子接口具有了父接口相同的行為;同時,子接口還可以在此基礎上自由發揮,添加屬于自己的行為。

以上,把“接口”換成“類”,結論同樣成立。讓我們來定義一個普通的父類 Wanger:

public class Wanger {

    int age;

    String name;

    void write() {

        System.out.println("我寫了本《基督山伯爵》");

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

public class Wangxiaoer extends Wanger{

    @Override

        System.out.println("我寫了本《茶花女》");

我們可以将通用的方法和成員變量放在父類中,達到代碼複用的目的;然後将特殊的方法和成員變量放在子類中,除此之外,子類還可以覆寫父類的方法(比如write() 方法)。這樣,子類也就煥發出了新的生命力。

Java 隻支援單一繼承,這一點,我在上一篇接口的文章中已經提到過了。如果一個類在定義的時候沒有使用 extends 關鍵字,那麼它隐式地繼承了 java.lang.Object 類——在我看來,這恐怕就是 Java 号稱萬物皆對象的真正原因了。

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

子類可以繼承父類的非 private 成員變量,為了驗證這一點,我們來看下面這個示例。

    String defaultName;

    private String privateName;

    public String publicName;

    protected String protectedName;

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

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

可以确認,除了私有的 privateName,其他三種類型的成員變量都可以繼承到。

同理,子類可以繼承父類的非 private 方法,為了驗證這一點,我們來看下面這個示例。

    private void privateWrite() {

    public void publicWrite() {

    protected void protectedWrite() {

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

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

可以确認,除了私有的 privateWrite(),其他三種類型的方法都可以繼承到。

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

    public Wanger(int age, String name) {

        this.age = age;

        this.name = name;

則必須在子類的構造器中顯式地通過 super 關鍵字進行調用,否則編譯器将提示以下錯誤:

修複後的代碼如下所示:

    public Wangxiaoer(int age, String name) {

        super(age, name);

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

    public static void main(String[] args) {

        Wanger wangxiaoer = new Wangxiaoer();

同理,子接口的實作類的對象引用類型也可以是一個父接口類型。

        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();

        System.out.println("睡着跑");

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

原本,default 方法就是為實作該接口而不覆寫該方法的類提供預設實作的,現在,相同方法簽名的 sleep() 方法把編譯器搞懵逼了,隻能重寫了。

public class Pig implements FlyInterface, RunInterface {

    public void fly() {

        System.out.println("會飛的豬");

    public void sleep() {

        System.out.println("隻能重寫了");

    public void run() {

        System.out.println("會跑的豬");

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

public interface WalkInterface extends FlyInterface,RunInterface{

    void walk();

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

原文位址

https://www.cnblogs.com/qing-gee/p/12990735.html