天天看點

Java基礎文法06-面向對象-繼承-this-super-就近原則-初始化

繼承

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

繼承的好處

  • 提高代碼的複用性。
  • 提高代碼的擴充性。
  • 類與類之間産生了關系,是學習多态的前提。如:學生 is - a 人。

繼承的格式(

extends

【修飾符】 class 父類 {

  ...

}

【修飾符】 class 子類 extends 父類 {

繼承的特點

1、Java隻支援單繼承,每一個子類隻有一個直接父類

2、Java支援多層繼承,父類還可以有父類

3、子類會繼承父類所有特征,包括成員變量、成員方法,哪怕是私有的,私有的在子類中無法直接使用。

4、子類不會繼承父類的代碼塊、構造器等。

5、但是子類一定會調用父類的執行個體初始化方法。

繼承的特點一:成員變量

1、父類成員變量私有化(private)

  • 父類中的成員,無論是公有(public)還是私有(private),均會被子類繼承。
  • 子類雖會繼承父類私有(private)的成員,但子類不能對繼承的私有成員直接進行通路,可通過繼承的get/set方法進行通路。如圖所示:
Java基礎文法06-面向對象-繼承-this-super-就近原則-初始化

子類建立對象時,在堆記憶體中是要為從父類繼承的成員變量配置設定記憶體空間的。

2、父類子類成員變量重名

如果子類出現與父類同名的成員變量會怎麼樣呢?

結論:

(1)當父類的成員變量私有化時,在子類中是無法直接通路的,是以是否重名不影響,如果想要通路父類的私有成員變量,隻能通過父類的get/set方法通路;

(2)當父類的成員變量非私有時,在子類中可以直接通路,是以如果有重名時,就需要加“super."進行差別。

格式:super.父類成員變量名,但是前提條件,這個成員變量沒有私有化。

說明:雖然我們可以區分父子類的重名成員變量,但是實際開發中,我們不建議這麼幹。

繼承的特點二:成員方法

我們說父類的所有方法子類都會繼承,但是當某個方法被繼承到子類之後,子類覺得父類原來的實作不适合于子類,該怎麼辦呢?

1、方法重寫

1.@Override:寫在方法上面,用來檢測是不是有效的正确覆寫重寫。這個注解就算不寫,隻要滿足要求,也是正确的方法覆寫重寫。建議保留

2.必須保證父子類之間方法的名稱相同,參數清單也相同。

3.子類方法的傳回值類型必須【小于等于】父類方法的傳回值類型(小于其實就是是它的子類,例如:Student < Person)。

注意:如果傳回值類型是基本資料類型和void,那麼必須是相同

4.子類方法的權限必須【大于等于】父類方法的權限修飾符。 小擴充:public > protected > 預設 > private

5.幾種特殊的方法不能被重寫

  • 靜态方法不能被重寫
  • 私有等在子類中不可見的方法不能被重寫
  • final方法不能被重寫

小貼士:重寫時,用到super.父類成員方法,表示調用父類的成員方法。

繼承的特點三:構造方法

當類之間産生了關系,其中各類中的構造方法,又産生了哪些影響呢?

首先我們要回憶兩個事情,構造方法的定義格式和作用。

  1. 構造方法的名字是與類名一緻的。

    是以子類是無法繼承父類構造方法的。

  2. 構造方法的作用是初始化執行個體變量的,而子類又會從父類繼承所有成員變量

    是以子類的初始化過程中,必須先執行父類的初始化動作。子類的構造方法中預設有一個

    super()

    ,表示調用父類的執行個體初始化方法,父類成員變量初始化後,才可以給子類使用。

如果父類沒有無參構造怎麼辦?

解決辦法:在子類構造器中,用super(實參清單),顯示調用父類的有參構造解決。

子類對象執行個體化過程中必須先完成從父類繼承的成員變量的執行個體初始化,這個過程是通過調用父類的執行個體初始化方法來完成的。

  • super():表示調用父類的無參執行個體初始化方法,要求父類必須有無參構造,而且可以省略不寫;
  • super(實參清單):表示調用父類的有參執行個體初始化方法,當父類沒有無參構造時,子類的構造器首行必須寫super(實參清單)來明确調用父類的哪個有參構造
  • super()和super(實參清單)都隻能出現在子類構造器的首行

繼承的特點四:單繼承限制

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

2、Java支援多層繼承(繼承體系)。

頂層父類是Object類。所有的類預設繼承Object,作為父類。

3、子類和父類是一種相對的概念。

4、一個父類可以同時擁有多個子類

final關鍵字

final:最終的,不可更改的,它的用法有:

1、修飾類

表示這個類不能被繼承,沒有子類

2、修飾方法

表示這個方法不能被子類重寫

3、聲明常量

final修飾某個變量(成員變量或局部變量),表示它的值就不能被修改,即常量,常量名建議使用大寫字母。

如果某個成員變量用final修飾後,沒有set方法,必須初始化(可以顯式指派、或在初始化塊指派、執行個體變量還可以在構造器中指派)指派

當修飾的是數組變量時,數組的位址值不可以改變但數組元素可以改變。

this關鍵字

this代表目前對象的引用(位址值),即對象自己的引用。

  • this可以用于非靜态代碼塊和構造器中:表示正在建立的那個執行個體對象,即正在new誰,this就代表誰
  • this用于執行個體方法中:表示調用該方法的對象,即誰在調用,this就代表誰。

this使用格式

1、this.成員變量名

當方法的局部變量與目前對象的成員變量重名時,就可以在成員變量前面加this.,如果沒有重名問題,就可以省略this.

2、this.成員方法

調用目前對象自己的成員方法時,都可以加"this.",也可以省略,實際開發中都省略

3、this()或this(實參清單)

當需要調用本類的其他構造器時,就可以使用該形式。

要求:

必須在構造器的首行

如果一個類中聲明了n個構造器,則最多有 n - 1個構造器中使用了"this(【實參清單】)",否則會發生遞歸調用死循環

super關鍵字

super代表父類的引用

注意:在子類中通過super去引用父類的成員時,必須保證該成員在子類中是仍然可見的,即注意權限修飾符問題

(1)super.成員變量

在子類中通路父類的成員變量,特别是當子類的成員變量與父類的成員變量重名時。

(2)super.成員方法

在子類中調用父類的成員方法,特别是當子類重寫了父類的成員方法時

(3)super()或super(實參清單)

在子類的構造器首行,用于表示調用父類的哪個構造器(本質上是該構造器對應的執行個體初始化方法)

super() 和 this() 都必須是在構造方法的第一行,是以不能同時出現。

this()和this(實參清單) 與 super()或super(實參清單) 不能同時出現

就近原則:解決複雜問題

  • 沒有super和this
    • 在構造器、代碼塊、方法中如果出現使用某個變量,先檢視是否是目前塊聲明的局部變量,
    • 如果不是局部變量,先從目前類去找成員變量
    • 如果目前類中沒有找到,會往上找父類的(非private,跨包還不能是預設的)
  • this :代表目前對象的引用。
    • 通過this找成員變量和成員方法時,先從目前類中找,沒有的會往上找父類的(非private,跨包還不能是預設的)。
    • 但是this()或this(實參清單)隻會在本類中找
  • super :代表父類的存儲空間辨別(可以了解為父類的引用)。
    • 通過super找成員變量和成員方法時,直接從父類空間(包含父類的父類繼承的)找
    • super()或super(實參清單)隻能從直接父類找
    • 通過super隻能通路父類在子類中可見的(非private,跨包還不能是預設的)
  • 即:super一定是從直接父類開始找

    this一定是從目前類的成員開始找

    即沒有this又沒有super,一定是從局部變量開始找

注意:super和this都不能出現在靜态方法和靜态代碼塊中,因為super和this都是存在與對象中的

繼承與初始化

類初始化

實際上,類初始化的過程是在調用一個<clinit>()方法,而這個方法是編譯器自動生成的。編譯器會将如下兩部分的所有代碼,按順序合并到類初始化<clinit>()方法體中。

(1)靜态類成員變量的顯式指派語句

(2)靜态代碼塊中的語句

整個類初始化隻會進行一次,如果子類初始化時,發現父類沒有初始化,那麼會先初始化父類。

每一個類都有一個類初始化方法<clinit>()方法,然後子類初始化時,如果發現父類沒有加載和沒有初始化,會先加載和初始化父類,然後再加載和初始化子類。一個類,隻會初始化一次。

執行個體初始化

實際上我們編寫的代碼在編譯時,會自動處理代碼,整理出一個<clinit>()的類初始化方法,還會整理出一個或多個的<init>(...)執行個體初始化方法。一個類有幾個執行個體初始化方法,由這個類有幾個構造器決定。

執行個體初始化方法的方法體,由四部分構成:

(1)super()或super(實參清單) 這裡選擇哪個,看原來構造器首行是哪句,沒寫,預設就是super()

(2)非靜态執行個體變量的顯示指派語句

(3)非靜态代碼塊

(4)對應構造器中的代碼

特别說明:其中(2)和(3)是按順序合并的,(1)一定在最前面(4)一定在最後面

執行特點:

  • 建立對象時,才會執行,
  • 調用哪個構造器,就是指定它對應的執行個體初始化方法
  • 建立子類對象時,父類對應的執行個體初始化會被先執行(父類的clinit<>(有參或無參)方法執行完後再執行子類的init<>(有參或無參)方法),執行父類哪個執行個體初始化方法,看用super()還是super(實參清單)

類初始化肯定優先于執行個體初始化。

類初始化隻做一次。

執行個體初始化是每次建立對象都要進行。