天天看點

Java基礎繼承與多态

Java基礎第九天

繼承概述

引入

首先我來寫兩個代碼:

我們觀察上面兩個代碼:

發現name,age成員變量,以及getXxx()/setXxx(),還有eat()等都是相同的。

如果我們後來繼續定義類,舉例:勞工類,軍人類。他們是不是也具備這些内容。

那麼,我們每一次定義這樣的類的時候,都要把這些重複的内容都重新定義一遍。

麻煩不?麻煩。是以,我們要考慮改進?

如何改進呢?

我這樣想的:我能不能把這些相同的内容給定義到一個獨立的類中。

然後,讓這多個類和這個獨立的類産生一個關系,有了這個關系後,

這多個類就可以具備這個獨立的類的功能。

為了實作這個效果,java就提供了一個技術:繼承。

父親:

4個兒子

繼承怎麼表示呢?繼承的格式是什麼樣子的呢?

我們就回頭修改我們的代碼:

多個類中存在相同屬性和行為時,将這些内容抽取到單獨一個類中,那麼多個類無需再定義這些屬性和行為,隻要繼承那個類即可。

通過extends關鍵字可以實作類與類的繼承

class 子類名 extends 父類名 {}  

單獨的這個類稱為父類,基類或者超類;這多個類可以稱為子類或者派生類。

有了繼承以後,我們定義一個類的時候,可以在一個已經存在的類的基礎上,還可以定義自己的新成員。

繼承的案例和繼承的好處

通過一個具體案例來示範代碼

案例1:學生類和老師。定義兩個功能(吃飯,睡覺)

案例2:加入人類後改進。

/*

繼承概述:

把多個類中相同的内容給提取出來定義到一個類中。

如何實作繼承呢?

Java提供了關鍵字:extends

格式:

class 子類名 extends 父類名 {}

好處:

A:提高了代碼的複用性

B:提高了代碼的維護性

C:讓類與類之間産生了關系,是多态的前提

*/

繼承的好處

提高了代碼的複用性

   多個類相同的成員可以放到同一個類中

提高了代碼的維護性

   如果功能的代碼需要修改,修改一處即可

讓類與類之間産生了關系,是多态的前提

Java中繼承的特點

Java隻支援單繼承,不支援多繼承。

一個類隻能有一個父類,不可以有多個父類。

class SubDemo extends Demo{} //ok

class SubDemo extends Demo1,Demo2...//error

Java支援多層繼承(繼承體系)

class A{}

class B extends A{}

class C extends B{}

Java中繼承的特點:

A:Java隻支援單繼承,不支援多繼承。

有些語言是支援多繼承,格式:extends 類1,類2,...

B:Java支援多層繼承(繼承體系)

class Father {}

class Mother {}

class Son exnteds Father {} //正确的

class Son extends Father,Mother {} // 錯誤的

Java中繼承的注意事項

子類隻能繼承父類所有非私有的成員(成員方法和成員變量)

  其實這也展現了繼承的另一個弊端:打破了封裝性

子類不能繼承父類的構造方法,但是可以通過super(後面講)關鍵字去通路父類構造方法。

不要為了部分功能而去繼承

我們到底在什麼時候使用繼承呢?

繼承中類之間展現的是:”is a”的關系。

繼承的注意事項:

A:子類隻能繼承父類所有非私有的成員(成員方法和成員變量)

B:子類不能繼承父類的構造方法,但是可以通過super(馬上講)關鍵字去通路父類構造方法。

C:不要為了部分功能而去繼承

class A {

public void show1(){}

public void show2(){}

}

class B {

public void show3(){}

//我們發現B類中出現了和A類一樣的show2()方法,是以,我們就用繼承來展現

class B extends A {

這樣其實不好,因為這樣你不但有了show2(),還多了show1()。

有可能show1()不是你想要的。

那麼,我們什麼時候考慮使用繼承呢?

繼承其實展現的是一種關系:"is a"。

Person

Student

Teacher

水果

蘋果

香蕉

橘子

采用假設法。

如果有兩個類A,B。隻有他們符合A是B的一種,或者B是A的一種,就可以考慮使用繼承。

繼承中成員變量的關系

案例示範

子父類中同名和不同名的成員變量

類的組成:

成員變量:

構造方法:

成員方法:

而現在我們又講解了繼承,是以,我們就應該來考慮一下,類的組成部分的各自關系。

繼承中成員變量的關系:

A:子類中的成員變量和父類中的成員變量名稱不一樣,這個太簡單。

B:子類中的成員變量和父類中的成員變量名稱一樣,這個怎麼玩呢?

在子類方法中通路一個變量的查找順序:

a:在子類方法的局部範圍找,有就使用

b:在子類的成員範圍找,有就使用

c:在父類的成員範圍找,有就使用

d:如果還找不到,就報錯。

繼承中構造方法的關系

子類中所有的構造方法預設都會通路父類中空參數的構造方法

為什麼呢?

因為子類會繼承父類中的資料,可能還會使用父類的資料。是以,子類初始化之前,一定要先完成父類資料的初始化。

每一個構造方法的第一條語句預設都是:super()

A:子類中所有的構造方法預設都會通路父類中空參數的構造方法

B:為什麼呢?

因為子類會繼承父類中的資料,可能還會使用父類的資料。

是以,子類初始化之前,一定要先完成父類資料的初始化。

注意:子類每一個構造方法的第一條語句預設都是:super();

如何父類中沒有構造方法,該怎麼辦呢?

子類通過super去顯示調用父類其他的帶參的構造方法

子類通過this去調用本類的其他構造方法

    本類其他構造也必須首先通路了父類構造

一定要注意:

super(…)或者this(….)必須出現在第一條語句上

否則,就會有父類資料的多次初始化

如果父類沒有無參構造方法,那麼子類的構造方法會出現什麼現象呢?

報錯。

如何解決呢?

A:在父類中加一個無參構造方法

B:通過使用super關鍵字去顯示的調用父類的帶參構造方法

C:子類通過this去調用本類的其他構造方法

子類中一定要有一個去通路了父類的構造方法,否則父類資料就沒有初始化。

注意事項:

this(...)或者super(...)必須出現在第一條語句上。

如果不是放在第一條語句上,就可能對父類的資料進行了多次初始化,是以必須放在第一條語句上。

面試題一

看程式寫結果:

A:成員變量就近原則

B:this和super的問題

this通路本類的成員

super通路父類的成員

C:子類構造方法執行前預設先執行父類的無參構造方法

D:一個類的初始化過程

成員變量進行初始化

預設初始化

顯示初始化

構造方法初始化

結果:

fu

zi

30

20

10

面試題二

A:一個類的靜态代碼塊,構造代碼塊,構造方法的執行流程

靜态代碼塊 > 構造代碼塊 > 構造方法

B:靜态的内容是随着類的加載而加載

靜态代碼塊的内容會優先執行

C:子類初始化之前先會進行父類的初始化

結果是:

靜态代碼塊Fu

靜态代碼塊Zi

構造代碼塊Fu

構造方法Fu

構造代碼塊Zi

構造方法Zi

繼承中成員方法的關系

     子父類中同名和不同名的成員方法

結論:

  通過子類對象去通路一個方法

  首先在子類中找

  然後在父類中找

  如果還是沒有就報錯。(不考慮父親的父親…)

繼承中成員方法的關系:

A:子類中的方法和父類中的方法聲明不一樣,這個太簡單。

B:子類中的方法和父類中的方法聲明一樣,這個該怎麼玩呢?

通過子類對象調用方法:

a:先找子類中,看有沒有這個方法,有就使用

b:再看父類中,有沒有這個方法,有就使用

c:如果沒有就報錯。

方法重寫概述

    子類中出現了和父類中一模一樣的方法聲明,也被稱為方法覆寫,方法複寫。

使用特點:

   如果方法名不同,就調用對應的方法

   如果方法名相同,最終使用的是子類自己的

方法重寫的應用:

  當子類需要父類的功能,而功能主體子類有自己特有内容時,可以重寫父類中的方法,這樣,即沿襲了父類的功能,又定義了子類特有的内容。

方法重寫:子類中出現了和父類中方法聲明一模一樣的方法。

方法重載:

本類中出現的方法名一樣,參數清單不同的方法。與傳回值無關。

子類對象調用方法的時候:

先找子類本身,再找父類。

當子類需要父類的功能,而功能主體子類有自己特有内容時,可以重寫父類中的方法。

這樣,即沿襲了父類的功能,又定義了子類特有的内容。

案例:

A:定義一個手機類。

B:通過研究,我發明了一個新手機,這個手機的作用是在打完電話後,可以聽天氣預報。

按照我們基本的設計,我們把代碼給寫出來了。

但是呢?我們又發現新手機應該是手機,是以,它應該繼承自手機。

其實這個時候的設計,并不是最好的。

因為手機打電話功能,是手機本身就具備的最基本的功能。

是以,我的新手機是不用在提供這個功能的。

但是,這個時候,打電話功能就沒有了。這個不好。

最終,還是加上這個功能。由于它繼承了手機類,是以,我們就直接使用父類的功能即可。

那麼,如何使用父類的功能呢?通過super關鍵字調用

方法重寫的注意事項

父類中私有方法不能被重寫

子類重寫父類方法時,通路權限不能更低

父類靜态方法,子類也必須通過靜态方法進行重寫。(其實這個算不上方法重寫,但是現象确實如此,至于為什麼算不上方法重寫,多态中我會講解)

A:父類中私有方法不能被重寫

因為父類私有方法子類根本就無法繼承

B:子類重寫父類方法時,通路權限不能更低

最好就一緻

C:父類靜态方法,子類也必須通過靜态方法進行重寫

其實這個算不上方法重寫,但是現象确實如此,至于為什麼算不上方法重寫,多态中我會講解

子類重寫父類方法的時候,最好聲明一模一樣。

面試題

1:方法重寫和方法重載的差別?方法重載能改變傳回值類型嗎?

方法重寫:

在子類中,出現和父類中一模一樣的方法聲明的現象。

同一個類中,出現的方法名相同,參數清單不同的現象。

方法重載能改變傳回值類型,因為它和傳回值類型無關。

Override:方法重寫

Overload:方法重載

2:this關鍵字和super關鍵字分别代表什麼?以及他們各自的使用場景和作用。

this:代表目前類的對象引用

super:代表父類存儲空間的辨別。(可以了解為父類的引用,通過這個東西可以通路父類的成員)

場景:

this.成員變量

super.成員變量

this(...)

super(...)

this.成員方法

super.成員方法

繼承練習

學生案例和老師案例講解

 使用繼承前

 使用繼承後

父類中成員private修飾,子類如何通路呢?

學生:

成員變量;姓名,年齡

構造方法:無參,帶參

成員方法:getXxx()/setXxx()

老師:

看上面兩個類的成員,發現了很多相同的東西,是以我們就考慮抽取一個共性的類:

人:

學生 繼承 人

老師 繼承 人

貓狗案例講解

分析和實作

先找到具體的事物,然後發現具體的事物有共性,才提取出一個父類。

貓:

成員變量:姓名,年齡,顔色

getXxx()/setXxx()

eat()

palyGame()

狗:

lookDoor()

共性:

把共性定義到一個類中,這個類的名字叫:動物。

動物類:

成員方法:palyGame()

成員方法:lookDoor()

final關鍵字

final關鍵字是最終的意思,可以修飾類,成員變量,成員方法。

  修飾類,類不能被繼承

  修飾變量,變量就變成了常量,隻能被指派一次

  修飾方法,方法不能被重寫

繼承的代碼展現

由于繼承中方法有一個現象:方法重寫。

是以,父類的功能,就會被子類給覆寫調。

有些時候,我們不想讓子類去覆寫掉父類的功能,隻能讓他使用。

這個時候,針對這種情況,Java就提供了一個關鍵字:final

final:最終的意思。常見的是它可以修飾類,方法,變量。

 Final特性

final可以修飾類,方法,變量

特點:

final可以修飾類,該類不能被繼承。

final可以修飾方法,該方法不能被重寫。(覆寫,複寫)

final可以修飾變量,該變量不能被重新指派。因為這個變量其實常量。

常量:

A:字面值常量

"hello",10,true

B:自定義常量

final int x = 10;

//final class Fu //無法從最終Fu進行繼承

final關鍵字面試題

final修飾局部變量

   在方法内部,該變量不可以被改變

   在方法聲明上,分别示範基本類型和引用類型作為參數的情況

          基本類型,是值不能被改變

          引用類型,是位址值不能被改變

面試題:final修飾局部變量的問題

基本類型:基本類型的值不能發生改變。

引用類型:引用類型的位址值不能發生改變,但是,該對象的堆記憶體的值是可以改變的。

final修飾變量的初始化時機

在對象構造完畢前即可

A:被final修飾的變量隻能指派一次。

B:在構造方法完畢前。(非靜态的常量)

多态

某一個事物,在不同時刻表現出來的不同狀态。

舉例:

  貓可以是貓的類型。貓 m = new 貓();

  同時貓也是動物的一種,也可以把貓稱為動物。

動物 d = new 貓();

 在舉一個例子:水在不同時刻的狀态

多态前提和展現

 有繼承關系

 有方法重寫

 有父類引用指向子類對象

多态案例及成員通路特點

多态:同一個對象(事物),在不同時刻展現出來的不同狀态。

貓是貓,貓是動物。

水(液體,固體,氣态)。

多态的前提:

A:要有繼承關系。

B:要有方法重寫。

其實沒有也是可以的,但是如果沒有這個就沒有意義。

d.show();

動物 d = new 狗();

C:要有父類引用指向子類對象。

父 f =  new 子();

用代碼展現一下多态。

多态中的成員通路特點:

A:成員變量

編譯看左邊,運作看左邊。

B:構造方法

建立子類對象的時候,通路父類的構造方法,對父類的資料進行初始化。

C:成員方法

編譯看左邊,運作看右邊。

D:靜态方法

(靜态和類相關,算不上重寫,是以,通路還是左邊的)

由于成員方法存在方法重寫,是以它運作看右邊。

成員變量

 編譯看左邊,運作看左邊

成員方法

 編譯看左邊,運作看右邊

靜态方法

  編譯看左邊,運作看左邊

 是以前面我說靜态方法不能算方法的重寫

多态的好處

提高了程式的維護性(由繼承保證)

提高了程式的擴充性(由多态保證)

多态的好處:

A:提高了代碼的維護性(繼承保證)

B:提高了代碼的擴充性(由多态保證)

貓狗案例代碼

多态的弊端

不能通路子類特有功能

那麼我們如何才能通路子類的特有功能呢?

多态中的轉型

多态的弊端:

不能使用子類的特有功能。

多态中的轉型問題

向上轉型

  從子到父

  父類引用指向子類對象

向下轉型

  從父到子

  父類引用轉為子類對象

我就想使用子類的特有功能?行不行?

行。

怎麼用呢?

A:建立子類對象調用方法即可。(可以,但是很多時候不合理。而且,太占記憶體了)

B:把父類的引用強制轉換為子類的引用。(向下轉型)

對象間的轉型問題:

向上轉型:

Fu f = new Zi();

向下轉型:

Zi z = (Zi)f; //要求該f必須是能夠轉換為Zi的。

多态成員通路及轉型的了解

多态的問題了解:

class 孔子爹 {

public int age = 40;

public void teach() {

System.out.println("講解JavaSE");

class 孔子 extends 孔子爹 {

public int age = 20;

System.out.println("講解論語");

public void playGame() {

System.out.println("英雄聯盟");

//Java教育訓練特别火,很多人來請孔子爹去講課,這一天孔子爹被請走了

//但是還有人來請,就剩孔子在家,價格還挺高。孔子一想,我是不是可以考慮去呢?

//然後就穿上爹的衣服,帶上爹的眼睛,粘上爹的胡子。就開始裝爹

//向上轉型

孔子爹 k爹 = new 孔子();

//到人家那裡去了

System.out.println(k爹.age); //40

k爹.teach(); //講解論語

//k爹.playGame(); //這是兒子才能做的

//講完了,下班回家了

//脫下爹的裝備,換上自己的裝備

//向下轉型

孔子 k = (孔子) k爹;

System.out.println(k.age); //20

k.teach(); //講解論語

k.playGame(); //英雄聯盟

多态記憶體圖

Java基礎繼承與多态

多态中對象變化的記憶體圖解

Java基礎繼承與多态

ClassCastException:類型轉換異常

一般在多态的向下轉型中容易出現

案例練習

多态練習:貓狗案例

不同地方飲食文化不同的案例

SouthPerson

NorthPerson