繼承

I. 繼承概述

1. 什么是繼承
?子類繼承父類的特征行為想诅,使得子類具有父類的各種屬性和方法召庞。或子類從父類繼承方法来破,使得子類具有父類相同的行為篮灼。
繼承的特點(diǎn):
* 在繼承關(guān)系中,父類更通用徘禁、子類更具體诅诱。父類具有更一般的特征和行為,而子類除了具有父類的特征和行為送朱,還具有一些自己特殊的特征和行為娘荡。
* 在繼承關(guān)系中。父類和子類需要滿足is-a的關(guān)系驶沼。子類是父類炮沐。

2. 為什么要繼承
?使用繼承可以有效實(shí)現(xiàn)代碼復(fù)用避免重復(fù)代碼的出現(xiàn)商乎。當(dāng)兩個(gè)類具有相同的特征(屬性)和行為(方法)時(shí)央拖,可以將相同的部分抽取出來(lái)放到一個(gè)類中作為父類,其它兩個(gè)類繼承這個(gè)父類鹉戚。

3.如何實(shí)現(xiàn)繼承
?在Java中鲜戒,用extends關(guān)鍵字來(lái)表示一個(gè)類繼承了另一個(gè)類。在父類中只定義一些通用的屬性和方法抹凳。子類繼承父類的屬性和方法遏餐,子類中可以定義特定的屬性和方法∮祝或子類重新定義父類的屬性失都、重寫(xiě)父類的方法可以獲得與父類不同的功能。

接下來(lái)以一個(gè)例子來(lái)說(shuō)明:

/**
 * 繼承的演示:
 * 工人和學(xué)生都是人幸冻,
 * 他們有共同的屬性:年齡和姓名
 *     有共同的行為:吃飯
 * 他們還有各自特有的屬性:工人有公司
 *                      學(xué)生有學(xué)校
 *        各自特有的行為:工人工作
 *                      學(xué)生學(xué)習(xí)
 */

/**
* @Date: 2017/11/11
* @Time: 下午9:02
* @Description: 父類
*/
class Person {
    String name;
    int age;
    void eat() {
        System.out.println("人可以吃飯");
    }
}

/**
* @Date: 2017/11/11
* @Time: 下午9:08
* @Description: 學(xué)生類
*/
class Student extends Person {
    String school;
    void study() {
        System.out.println("學(xué)生學(xué)習(xí)");
    }
}

/**
* @Date: 2017/11/11
* @Time: 下午9:09
* @Description: 工人類
*/
class Worker extends Person {
    String company;
    void work() {
        System.out.println("工人工作");
    }
}

/**
 * @Author: 落腳丶
 * @Date: 2017/11/11
 * @Time: 下午9:02
 * @Description: 繼承演示
 */
public class InheritanceDemo {
    public static void main(String[] args) {
        Student student = new Student();
        student.age = 20; // 具有父類的屬性年齡
        student.name = "Jean"; // 具有父類的屬性姓名
        student.school = "No1 Middle School"; // 子類獨(dú)有的屬性
        System.out.println(
                "學(xué)生的年齡:" + student.age + "\n" +
                "學(xué)生的姓名:" + student.name +"\n" +
                "學(xué)生所在學(xué)校:" + student.school
        );
        student.eat(); // 調(diào)用父類的方法
        student.study(); // 調(diào)用子類特有方法
    }
}
/**
 * Output
 * 學(xué)生的年齡:20
 * 學(xué)生的姓名:Jean
 * 學(xué)生所在學(xué)校:No1 Middle School
 * 人可以吃飯
 * 學(xué)生學(xué)習(xí)
 */

上面僅僅是繼承的基本概念粹庞,下面我們?cè)賮?lái)探究一下繼承的細(xì)節(jié)。


II. 繼承的細(xì)節(jié)

  • 單繼承與多重繼承
    Java支持單繼承洽损,不直接支持多繼承庞溜。即,一個(gè)子類只能有一個(gè)直接父類碑定,不能有多個(gè)直接父類流码。
class A {
    void show() {
        System.out.println("a");
    }
}
class B {
    void show() {
        System.out.println("b");
    }
}
class C extends A,B {}

上面這中情況是不允許的又官,我們也很容易能看出來(lái)如果可以這么做帶來(lái)的問(wèn)題:如果類C同時(shí)繼承了類A和類B,那么當(dāng)C調(diào)用show()方法時(shí)漫试,就會(huì)出現(xiàn)不確定性六敬。

Java支持多重繼承,即下面的情況是被允許的驾荣。

class A {}

class B extends A {}

class C extends B {}
  • 成員變量

由第一個(gè)例子我們可以知道外构,子類繼承了父類的成員變量,并且可以直接使用秘车。子類還可以定義自己的成員變量典勇。不過(guò),這里會(huì)出現(xiàn)一個(gè)特殊的情況:子父類中有同名的成員變量叮趴。
看下面的例子:

class A {
    int tem = 1;
    int num = 4;
}

class B extends A {
    int num = 5;
    void show() {
        System.out.println("num = " + num + "\n" +
                "tem = " + tem
        );
    }
}

public class InheritanceDemo {
    public static void main(String[] args) {
        B b = new B();
        b.show();
    }
}
/**
 * Output
 * num = 5
 * tem = 1
 */

由上面例子可以看出,子類B的對(duì)象調(diào)用show方法時(shí)权烧,輸出num = 5眯亦;首先我們要明確的一點(diǎn)是雖然子類和父類中都有變量num,但是它們不是同一個(gè)變量般码。所以妻率,并不是子類的num重新定義的了父類num的值。這涉及到作用域的問(wèn)題板祝,子類優(yōu)先調(diào)用子類的變量宫静。如果子類和父類中有同名的變量,我們還想調(diào)用父類的變量券时,可以使用super關(guān)鍵字孤里。

class A {
    int tem = 1;
    int num = 4;
}

class B extends A {
    int num = 5;
    void show() {
        System.out.println("num = " + super.num + "\n" +
                "tem = " + tem
        );
    }
}

public class InheritanceDemo1 {
    public static void main(String[] args) {
        B b = new B();
        b.show();
    }
}
/**
 * Output
 * num = 4
 * tem = 1
 */

這樣,就可以使用父類的成員變量了橘洞。

  • 成員方法
    正常情況下捌袜,子類可以調(diào)用從父類中繼承的方法。
class A {
    void show1() {
        System.out.println("A show() run");
    }
}

class B extends A {
    void show2() {
        System.out.println("B show() run");
    }
}

public class InheritanceDemo {
    public static void main(String[] args) {
        B b = new B();
        b.show1();
        b.show2();
    }
}
/**
 * Output
 * A show() run
 * B show() run
 */

我們現(xiàn)在重點(diǎn)要說(shuō)的是當(dāng)子父類中出現(xiàn)相同的方法時(shí)炸枣,這時(shí)就是出現(xiàn)重寫(xiě)(override)虏等。(也稱覆蓋覆寫(xiě),因?yàn)檫@時(shí)父類的方法不能再由子類對(duì)象通過(guò)“.”運(yùn)算符直接被調(diào)用适肠,就像被覆蓋了一樣霍衫。)
方法的重寫(xiě)有三點(diǎn)需要注意:

  1. 在子類中可以根據(jù)需要對(duì)從基類中繼承來(lái)的方法進(jìn)行重寫(xiě)。
  2. 重寫(xiě)的方法和被重寫(xiě)的方法必須具有相同方法名稱侯养、參數(shù)列表和返回類型敦跌。
  3. 重寫(xiě)方法不能使用比被重寫(xiě)的方法更嚴(yán)格的訪問(wèn)權(quán)限。

接下來(lái)我們依次說(shuō)明一下這三點(diǎn)沸毁。
第一點(diǎn)說(shuō)明當(dāng)我們繼承父類的方法時(shí)峰髓,發(fā)現(xiàn)父類的方法不能滿足我們的要求傻寂,我們可以在子類中對(duì)父類的方法進(jìn)行重寫(xiě)。即在子類中寫(xiě)一個(gè)和父類一模一樣的方法携兵。這里需要注意的一點(diǎn)是疾掰,這個(gè)方法必須時(shí)從父類繼承過(guò)來(lái)的。

class A {
    private void show() {
        System.out.println("A show() run");
    }
}

class B extends A {
    public void show() {
        System.out.println("B show() run");
    }
}

public class InheritanceDemo {
    public static void main(String[] args) {
        B b = new B();
        b.show();
    }
}
/**
 * Output
 * B show() run
 */

我們知道徐紧,當(dāng)方法聲明為private權(quán)限時(shí)静檬,是不能被子類繼承的。上面的情況并级,子類并未從父類中繼承show()方法拂檩,所以該情況不是重寫(xiě)。

第二點(diǎn)就是我之前講的嘲碧,必須是同一個(gè)方法稻励。不光要有相同的方法名,還要有相同的參數(shù)列表愈涩。滿足這兩個(gè)要求的情況下望抽,返回值類型不一樣本身在Java中就是不被允許的。所以履婉,同一個(gè)函數(shù)必須滿足這三個(gè)要求煤篙。

class A {
    public void show(int num) {
        System.out.println("A show() run" + num);
    }
}

class B extends A {
    public void show() {
        System.out.println("B show() run");
    }
}

public class InheritanceDemo {
    public static void main(String[] args) {
        int a = 0;
        B b = new B();
        b.show(a);
        b.show();
    }
}
/**
 * Output
 * A show() run0
 * B show() run
 */

上面這種情況并沒(méi)有發(fā)生重寫(xiě),可以看到毁腿,對(duì)象b依然可以調(diào)用父類的show方法辑奈。

第三點(diǎn),重寫(xiě)函數(shù)的權(quán)限不能比父類中的權(quán)限更嚴(yán)格已烤。

class A {
    public void show() {
        System.out.println("A show() run");
    }
}

class B extends A {
    void show() {
        System.out.println("B show() run");
    }
}

public class InheritanceDemo1 {
    public static void main(String[] args) {
        B b = new B();
        b.show();
    }
}

即上面這種情況是不能被允許的鸠窗。編譯器被報(bào)錯(cuò):



那么如果我們還需要調(diào)用父類的show方法,可以在子類中通過(guò)super調(diào)用草戈。

class A {
    public void show() {
        System.out.println("A show() run");
    }
}

class B extends A {
    public void show() {
        super.show();
        System.out.println("B show() run");
    }

}

public class InheritanceDemo1 {
    public static void main(String[] args) {
        B b = new B();
        b.show();
    }
}
/**
 * A show() run
 * B show() run
 */
  • 構(gòu)造方法
    先看一個(gè)簡(jiǎn)單的例子:
class A {
    A() {
        System.out.println("A is running");
    }
}

class B extends A {
    B() {
        System.out.println("B is running");
    }
}

public class InheritanceDemo1 {
    public static void main(String[] args) {
        B b = new B();
    }
}
/**
 * A is running
 * B is running
 */

我們可以看出塌鸯,當(dāng)我們初始化子類對(duì)象時(shí),父類的構(gòu)造方法也會(huì)被調(diào)用唐片。這是為什么呢丙猬?原來(lái),在子類的構(gòu)造方法(不管有沒(méi)有參數(shù))中會(huì)隱式調(diào)用父類的空參構(gòu)造方法费韭。即茧球,實(shí)際情況是這樣的

class B extends A {
    B() {
        super();
        System.out.println("B is running");
    }
}
super();

該語(yǔ)句寫(xiě)不寫(xiě)都是存在的。此處需要注意的是星持,默認(rèn)調(diào)用的是父類的空參構(gòu)造方法抢埋,如果父類中沒(méi)有空參構(gòu)造方法就會(huì)報(bào)錯(cuò)。即,下面這種情況是不被允許的揪垄。

class A {
    A(int a) {
        System.out.println("A is running");
    }
}

class B extends A {
    B() {
        System.out.println("B is running");
    }
}

public class InheritanceDemo1 {
    public static void main(String[] args) {
        B b = new B();
    }
}

其實(shí)這也是很容易理解的穷吮,子類構(gòu)造之前為什么要先調(diào)用父類的構(gòu)造方法呢?因?yàn)榧⑴宇愐^承父類的成員捡鱼,所以在繼承之前,應(yīng)當(dāng)對(duì)父類先初始化酷愧。這樣我們就明白了驾诈,子類構(gòu)造之前,一定是要先構(gòu)造父類的溶浴。只是在默認(rèn)情況下調(diào)用了空參構(gòu)造乍迄。其實(shí)在更多的情況下,應(yīng)該是由人為指定的士败。并且super語(yǔ)句必須放在第一行闯两。如果子類的一個(gè)構(gòu)造方法中使用了this語(yǔ)句調(diào)用其他構(gòu)造方法,此時(shí)該構(gòu)造函數(shù)中super語(yǔ)句就沒(méi)有了谅将。

class A {
    A(int a) {
        System.out.println("A is running" + a);
    }
}

class B extends A {
    B() {
        super(4);
        System.out.println("B is running");
    }
}

public class InheritanceDemo1 {
    public static void main(String[] args) {
        B b = new B();
    }
}
/**
 * A is running4
 * B is running
 */
  • 子類實(shí)例化過(guò)程
    我們先來(lái)看下面這個(gè)例子
class A {
    A() {
        show();
    }
    void show() {
        System.out.println("A show");
    }
}

class B extends A {
    int num = 8;
    B() {
        super();
        System.out.println("B constructor..." + num);
    }
    void show() {
        System.out.println("B show..." + num);
    }
}

public class InheritanceDemo1 {
    public static void main(String[] args) {
        B b = new B();
        b.show();
    }
}
/**
 * Output
 * B show...0
 * B constructor...8
 * B show...8
 */

這個(gè)結(jié)果時(shí)有的人可能會(huì)很奇怪生蚁,那我們就來(lái)分析一下實(shí)例化的過(guò)程。
當(dāng)new一個(gè)B的對(duì)象時(shí):

  1. 首先會(huì)隱式(默認(rèn))初始化num = 0戏自;
  2. 然后調(diào)用B的構(gòu)造方法B();
  3. 進(jìn)入構(gòu)造方法后伤锚,首先執(zhí)行super()擅笔,即調(diào)用父類構(gòu)造方法;
  4. 父類(其實(shí)也有父類Object這里先忽略)的構(gòu)造方法調(diào)用show()方法屯援,注意這個(gè)show()方法是子類的show()方法猛们。因?yàn)檎麄€(gè)過(guò)程的主體是子類,調(diào)用時(shí)狞洋,子類的show()方法重寫(xiě)了父類的show()方法弯淘。所以會(huì)出現(xiàn)第一個(gè)輸出的結(jié)果;
  5. 當(dāng)父類的構(gòu)造方法結(jié)束后吉懊,接著對(duì)子類的變量進(jìn)行顯式初始化庐橙,此時(shí)num = 8;
  6. 然后構(gòu)造方法中再輸出num的時(shí)候買就會(huì)出現(xiàn)第二句輸出的情況借嗽。
  • final關(guān)鍵字與繼承
    當(dāng)用final修飾一個(gè)類時(shí)态鳖,表明這個(gè)類不能被繼承。也就是說(shuō)恶导,如果一個(gè)類你永遠(yuǎn)不會(huì)讓它被繼承浆竭,就可以用final進(jìn)行修飾。final類中的成員變量可以根據(jù)需要設(shè)為final,但是要注意final類中的所有成員方法都會(huì)被隱式地指定為final方法邦泄。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末删窒,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子顺囊,更是在濱河造成了極大的恐慌肌索,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,123評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件包蓝,死亡現(xiàn)場(chǎng)離奇詭異驶社,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)测萎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門亡电,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人硅瞧,你說(shuō)我怎么就攤上這事份乒。” “怎么了腕唧?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,723評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵或辖,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我枣接,道長(zhǎng)颂暇,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,357評(píng)論 1 283
  • 正文 為了忘掉前任但惶,我火速辦了婚禮耳鸯,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘膀曾。我一直安慰自己县爬,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,412評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布添谊。 她就那樣靜靜地躺著财喳,像睡著了一般。 火紅的嫁衣襯著肌膚如雪斩狱。 梳的紋絲不亂的頭發(fā)上耳高,一...
    開(kāi)封第一講書(shū)人閱讀 49,760評(píng)論 1 289
  • 那天,我揣著相機(jī)與錄音喊废,去河邊找鬼祝高。 笑死,一個(gè)胖子當(dāng)著我的面吹牛污筷,可吹牛的內(nèi)容都是我干的工闺。 我是一名探鬼主播乍赫,決...
    沈念sama閱讀 38,904評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼陆蟆!你這毒婦竟也來(lái)了雷厂?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,672評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤叠殷,失蹤者是張志新(化名)和其女友劉穎改鲫,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體林束,經(jīng)...
    沈念sama閱讀 44,118評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡像棘,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,456評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了壶冒。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片缕题。...
    茶點(diǎn)故事閱讀 38,599評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖胖腾,靈堂內(nèi)的尸體忽然破棺而出烟零,到底是詐尸還是另有隱情,我是刑警寧澤咸作,帶...
    沈念sama閱讀 34,264評(píng)論 4 328
  • 正文 年R本政府宣布锨阿,位于F島的核電站,受9級(jí)特大地震影響记罚,放射性物質(zhì)發(fā)生泄漏墅诡。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,857評(píng)論 3 312
  • 文/蒙蒙 一桐智、第九天 我趴在偏房一處隱蔽的房頂上張望书斜。 院中可真熱鬧,春花似錦酵使、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,731評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至穿撮,卻和暖如春缺脉,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背悦穿。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,956評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工攻礼, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人栗柒。 一個(gè)月前我還...
    沈念sama閱讀 46,286評(píng)論 2 360
  • 正文 我出身青樓礁扮,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子太伊,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,465評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容