接上篇博文—— "《詳解 繼承(上)—— 工具的抽象與分層》" 廢話不多說,進入正題: 本人在上篇“故弄玄虛”,用super();解決了問題,這是為什麼呢? 答曰:子類中所有的構造方法 預設 都會訪問父類中 空參數的構造方法 (拓展:由於這個原理,我們今後所做的“ 工具類 ”都必須要帶上無參構造) ...
接上篇博文——《詳解 繼承(上)—— 工具的抽象與分層》
廢話不多說,進入正題:
本人在上篇“故弄玄虛”,用super();解決了問題,這是為什麼呢?
答曰:子類中所有的構造方法預設都會訪問父類中空參數的構造方法
(拓展:由於這個原理,我們今後所做的“工具類”都必須要帶上無參構造)
那麼,父類沒有無參構造方法,子類怎麼辦?
解決父類沒有無參構造的手段:
- 在父類中添加一個無參的構造方法
- 子類通過super去調用父類其他的帶參的構造方法
- 子類通過this去調用本類的其他構造方法 (本類其他構造也必須首先訪問了父類構造)
那麼,現在,本人來講解一下super吧:
super:
super class 其實就是超類、基類、父類的意思。
在這裡本人來提醒一點:
Object類 是 所有類的基類
super有兩個嚴格要求:
- 只能出現在構造方法中;
- 如果有super(),則它必須是構造方法的第一條語句
(所以,super() 和 this() 不能同時出現在同一個構造方法中)
而且,不論我們在 子類 中的構造方法是 無參 還是 帶參,在預設情況下,JVM只調用基類的無參構造方法!
我們在上篇博文開頭就提到過,我們在“繼承”的過程中可以“擇優繼承”,那麼,本人現在就來講解下,如何“擇優繼承”:
我們實現這個結果的方法是:方法的覆蓋
那麼,現在我們對於方法的覆蓋進行以下說明:
(1)僅存在於有繼承關係的類之間;
(2)子類的方法名名稱,參數個數和類型,必須和被覆蓋的父類保持一致;
(3)子類的返回值必須和被覆蓋的父類保持一致;
(4)子類方法的修飾符不能“低於”被覆蓋的分類方法;
(5)若違反了(2),則實質上是方法的重載,並非覆蓋
(本人在這裡只是為了使同學們能瞭解後面的代碼,僅在這裡淺談覆蓋,在本人後續博文中會對於方法的覆蓋進行深度講解)
現在我們來舉一個簡單有趣的例子:
我們先建立一個包 com.mec.about_override.demo,併在包下建立如下類:
Animal.java:
package com.mec.about_override.demo;
public class Animal {
private String name;
public Animal(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void cry() {
System.out.println("動物的叫聲!");
}
}
Dog.java:
package com.mec.about_override.demo;
public class Dog extends Animal {
public Dog(String name) {
super(name);
}
public void cry() {
System.out.println("汪汪");
}
}
Demo.java:
package com.mec.about_override.demo;
public class Demo {
public static void main(String[] args) {
Animal animal =new Animal("動物");
animal.cry();
Dog dog = new Dog("二愣子");
dog.cry();
}
}
我們現在來編譯一下,結果如下:
可以看出,我們在 Animal類中所編寫的 cry() 方法被覆蓋了!
多態:
現在,本人來介紹本篇博文的另一個知識點 —— 多態:
多態 —— 某一個事物,在不同時刻表現出來的不同狀態
首先,多態是有 條件的:
多態前提:
- 要有繼承關係
- 要有覆蓋(方法重寫)
其實沒有也是可以的,但是如果沒有這個就沒有意義- 要有父類引用指向子類對象
形如:
父 f = new 子();
接下來,本人來講解一個非常有趣的 知識點:
基類 與 派生類 之間的 強制類型轉換:
我們對上面的 Dog類 和 Demo類 做如下修改:
Dog.java:
package com.mec.about_override.demo;
public class Dog extends Animal {
public Dog(String name) {
super(name);
}
public void cry() {
System.out.println("汪汪");
}
public void dogAction() {
System.out.println("狗子快跑!");
}
}
Demo.java:
package com.mec.about_override.demo;
public class Demo {
public static void main(String[] args) {
Animal animal =new Animal("動物");
animal.cry();
Dog dog = new Dog("二愣子");
dog.cry();
Animal otherAnimal = (Animal) dog;
dog.dogAction();
dog.cry();
}
}
運行結果如下:
現在可能就有同學有疑問了這裡的輸出結果竟然是“汪汪”而不是“動物的叫聲”!
這裡對上述問題做出解釋:
基類 與 派生類 之間的 強制類型轉換 遵循如下原則:
- 對象的類型 約束 對象所能引用的 成員 和 方法,但是,不能更改 成員 和 方法 的本質內容;
- 對於方法,強轉不能改變 其所 實際指向 的 代碼 的首地址; 對象 的類型 決定 對象 所能引用的 對象和方法 的種類(即:Animal類型); 對象 所能調用的 成員 和 方法 取決於所申請空間的 對象和方法(即:Dog類型);
- 子類對象 可以被強轉成 父類類型,但父類對象 不能被強轉成 子類類型,因為父類對象中可能不存在子類類型的成員和方法
對於以上的現象,可能同學們在初學時會對子類與基類之間的關係感覺有點頭暈。
別怕,在這裡,本人還要介紹一個知識點,來輔助我們識別它們之間的關係:
多態中的成員訪問特點:
- 成員變數 :
編譯看左邊,運行看左邊。- 構造方法 :
創建子類對象的時候,會訪問父類的構造方法,對父類的數據進行初始化。- 成員方法 :
編譯看左邊,運行看右邊。- 靜態方法 :
編譯看左邊,運行看左邊。
(靜態和類相關,算不上重寫,所以,訪問還是左邊的)
那麼,為什麼要存在多態這個機制呢?
多態的好處:
- 提高了代碼的維護性(繼承保證)
- 提高了代碼的擴展性(由多態保證)
那麼,繼承的所有知識點也就講解完成了,這篇博文寫完已經是當天的凌晨3:17了,本人也是實在困得不行了,可見,兢兢業業的良心博主啊(哈哈,這裡小小抱怨一下,勿怪)。
若是對上述知識點或代碼有任何疑惑、意見或者建議,請在下方評論區提出,本人將儘早予以答覆,覺得有所幫助的同學請留下小贊贊,謝謝!!!