1. 程式人生 > >JAVA學習---面向物件之繼承·多型

JAVA學習---面向物件之繼承·多型

這週六我們學習了面向物件的一些知識,包含繼承和多型的。
以下是我學過後的心得總結:

封裝:

即將構造方法,成員變數,成員方法封裝在一起,形成一個類class檔案,實現呼叫。


繼承:

  • 繼承概述
    繼承即多個類中存在相同的屬性和行為時,我們可以將這些內容抽取到單獨的一個類中,這個類即父類,剩餘的類則無需再定義這些屬性和行為,只需繼承那個類即可,這些類就是父類的子類。

  • 繼承語法

    class 子類名 extends 父類名{}

  • 繼承特點
    1.JAVA類只支援單繼承,但支援多層繼承。
    2.子類只能繼承父類所有非私有成員(成員方法和成員變數)。
    3.子類不能繼承父類的構造方法,但是我們可以通過super關鍵字去訪問父類的構造方法。
    4.子類在建立物件時,一定會先初始化父類的資料(執行父類的空參構造)。
    5.Object類是所有類的頂層父類,所有類都是直接或間接繼承與它。

  • 繼承的利弊
    1.優點:提高了程式碼的複用性和維護性,同時讓類魚類之間產生了關係,這是多型的前提。
    2.弊端:類的耦合性增強了。而我們開發的原則是高內聚,低耦合。

  • this和super的區別和應用
    this 代表的是本類物件的引用;
    super 代表的父類儲存空間的標識,是父類的引用,可以操作父類的成員。

現在我們來看一個程式碼,它結合了繼承的運用,同時體現了super和this的區別。

public class Fu {
    int num = 100;
    int num2=50;
    public void fu(){
        System.
out.println("我是父類"); } } public class Zi extends Fu { int num = 10; int c = 666; public void show(int num) { num = 123; System.out.println(num); System.out.println(this.num); //當子類的成員變數和父類重名的時候,可以用一個關鍵super來區分 //this 代表本類的引用。 //super 代表父類空間的一個表示,你可以通過super可以訪問父類的成員變數 成員方法,構造方法
System.out.println(super.num); System.out.println(c); System.out.println(num2); } public void hehe() { //呼叫父類的方法 this.fu(); //super訪問父類的成員方法 super.fu(); } } public class Test { public static void main(String[] args) { //變數的尋找 遵循就近訪問原則。先在區域性範圍找,找到就使用,如果沒找到就去本類成員範圍找,如果還沒找到,就去父類的成員找// Zi zi = new Zi(); zi.show(1); zi.hehe(); } }

在這裡插入圖片描述
從上面的結果看出來子類 Zi 繼承了父類 Fu ,test方法最後new了一個子類物件zi用它呼叫了其自身的show方法和hehe方法,在子類中hehe方法通過this和super方法呼叫訪問fu方法,this是在自己類中找不到了,所以去父類中去找fu方法,super則是直接去父類中尋找fu方法

  • 方法重寫
    即子類中出現了和父類一模一樣的宣告方法(方法名,引數列表,返回值型別),也被稱為方法覆蓋,方法重寫。
    當子類需要父類的功能,而功能主體子類有自己特有的內容時,就用到了方法重寫。用這種特性即沿襲了父類的功能,又定義了子類特有的內容。

  • 方法重寫的注意事項:
    1.父類中私有方法不能被重寫,應為它根本就不能被繼承,所以談不上重寫。
    2.子類重寫父類方法時,訪問許可權不能更低,最好與父類一致。
    3.父類的靜態方法,子類也必須通過靜態方法進行重寫。
    重寫父類的快捷鍵 CTRL+O

  • final關鍵字
    由於繼承中有一個方法重寫的現象,而有時候我們不想讓子類去重寫父類的方法,所以我們就用final關鍵字定義父類,這樣子類就沒法繼承了。

    final修飾特點:
    修飾類: 被修飾類不能被繼承
    修飾方法: 被修飾的方法不能被重寫
    修飾變數: 被修飾的變數不能被重新賦值

  • 程式碼塊
    在Java中,使用{}括起來的程式碼被稱為程式碼塊。
    根據其位置和宣告的不同,可以分為區域性程式碼塊,構造程式碼塊,靜態程式碼塊,同步程式碼塊等等。
    a:區域性程式碼塊
    在方法中出現;限定變數生命週期,及早釋放,提高記憶體利用率
    b:構造程式碼塊
    在類中方法外出現;多個構造方法方法中相同的程式碼存放到一起,每次呼叫構造都執行,並且在構造方法前執行
    c:靜態程式碼塊
    在類中方法外出現,並加上static修飾;用於給類進行初始化,在載入的時候就執行,並且只執行一次。

class Fu {
    static {
        System.out.println("靜態程式碼塊Fu"); 
    }

    {
        System.out.println("構造程式碼塊Fu");
    }

    public Fu() {
        System.out.println("構造方法Fu");
    }
}

class Zi extends Fu {
    static {
        System.out.println("靜態程式碼塊Zi"); //2
    }

    {
        System.out.println("構造程式碼塊Zi");
    }

    public Zi() {
        System.out.println("構造方法Zi");
    }
}

public class Test8 {

    static {
        System.out.println("測試類的靜態程式碼塊");
    }

    static {
        System.out.println("測試類的靜態程式碼塊2");
    }

    public static void main(String[] args) {
        Zi z = new Zi();
    }
}

實驗結果:
在這裡插入圖片描述

由上面的實驗結果我們可以看出,相同等級下,構造程式碼塊優先於構造方法執行,每建立一次物件,都要執行,而靜態程式碼塊,隨著類的載入而載入,只執行一次。


多型

  • 概念
    某一個事物,在不同時刻表現出來的不同狀態。

Cat c=new Cat(); //貓可以是貓的型別。
Animal a=new Cat(); //貓也是動物的一種

  • 多型前提
    1.要有繼承關係,否則談不上多型。
    2.要有方法重寫,沒有也可以,但是如果沒有就沒有多型的意義。
    3.要有父類引用指向子類物件。

  • 多型的訪問成員特點!!!
    1.多型訪問成員變數的特點:
    編譯看 左邊,執行看 左邊
    2.多型訪問構造方法的特點:
    建立子類物件的時候,會先對父類的資料進行初始化並訪問父類。
    3.多型訪問成員方法的特點:
    編譯看左邊,執行看右邊
    4.多型訪問靜態方法的特點:
    編譯看左邊,執行看左邊
    (靜態本來就算不上重寫,所以訪問還是看左邊的)

  • 多型的利弊
    首先多型提高了程式碼的維護性,這由繼承的特性保證。其次提高了程式碼的拓展性,這是由多型保證。
    但是多型不能使用子類的特有功能,我們必須通過向下轉型來解決此問題。

而向下轉型也是有風險的,時常不注意時就會犯型別轉換異常的錯誤,下面我們為您用一個例子來看看空指標異常的錯誤原理。

//父類定義Fu
public class Fu {

    public void show(){
        System.out.println("fu show");
    }
}
//子類定義Zi,同時定義了子類獨有的方法ziMethod()
public class Zi extends Fu {							//繼承了父類Fu

    @Override
    public void show() {
        System.out.println("zi show");
    }

    //提供一個子類獨有的方法
    public void ziMethod(){
        System.out.println("我是子類一個特有的一個方法");
    }
}
//子類定義Son,同時定義了子類獨有的方法sonMethod()
public class Son extends Fu{  					//繼承了父類Fu

    @Override
    public void show() {
        System.out.println("son  show");
    }

    **public void sonMethod(){
        System.out.println("我是Son 裡面的一個特有的方法");
    }**
}

按照多型的特性,父類引用指向子類的方法,父類不能使用子類的特有方法,我們需要向下轉型。

public class Test5 {
    public static void main(String[] args) {
        Fu fu = new Zi();
        //fu.ziMethod();                                 這裡就會報錯
        //向下轉型
        Zi zi = (Zi) fu;
        zi.ziMethod();
        fu = new Son();
        fu.show();
        Son son = (Son) fu;
        son.sonMethod();
        System.out.println("---------------------------");
        Fu fu1 = new Zi();
        Zi zi1 = (Zi) fu;						//ClassCastException 型別轉換異常
        zi1.ziMethod();
        Son son1 = (Son) fu1;
        son1.sonMethod();
    }
}

在這裡插入圖片描述
從以上結果我們可以看出,這個測試類就犯了型別轉換異常,而原理就是
fu = new Son();
fu.show();
Son son = (Son) fu;

在這裡我們先把fu物件向下轉型為了Son型別的物件,而在後面我們又
Zi zi1 = (Zi) fu;
zi1.ziMethod();

要把fu物件向下轉型為Zi類物件,而它在上面已經被轉型為Son的物件,Son和Zi都屬於子類,不存在直接的繼承關係,所以無法轉型,而我們新定義的
Fu fu1 = new Zi();
fu1就可以向下轉型為son,從而呼叫son特有的方法sonMethon();