遇到認真的讀者是作者的一種幸運,真的,上一篇介面推送后,有好幾個讀者留言說,“二哥,你有一處內容需要修正,應該是介面中不能有 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 所不具有的功能,
public interface OneInterface extends 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
void write() {
System.out.println("我寫了本《茶花女》");
}
}
我們可以將通用的方法和成員變數放在父類中,達到代碼復用的目的;然后將特殊的方法和成員變數放在子類中,除此之外,子類還可以覆寫父類的方法(比如write() 方法),這樣,子類也就煥發出了新的生命力,
Java 只支持單一繼承,這一點,我在上一篇介面的文章中已經提到過了,如果一個類在定義的時候沒有使用 extends 關鍵字,那么它隱式地繼承了 java.lang.Object 類——在我看來,這恐怕就是 Java 號稱萬物皆物件的真正原因了,
那究竟子類繼承了父類的什么呢?
子類可以繼承父類的非 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 FlyInterface, RunInterface {
@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();
}
學到了吧?學到就是賺到,
我是沉默王二,一枚有趣的程式員,如果覺得文章對你有點幫助,請微信搜索「 沉默王二 」第一時間閱讀,回復【666】更有我為你精心準備的 500G 高清教學視頻(已分門別類),
本文 GitHub 已經收錄,有大廠面試完整考點,歡迎 Star,
原創不易,莫要白票,請你為本文點個贊吧,這將是我寫作更多優質文章的最強動力,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/177750.html
標籤:Java
上一篇:終于有人把最適合學習演算法的書單找出來了,面試必備!
下一篇:Slf4j初探
