day9 9 類的繼承與多態的總結

9、類的繼承與多態的內容小結

學好繼承和多態是面向對象開發語言中非常重要的一個環節。如果在程序中使用繼承和多態得當,整個程序的架構將變得非常有彈性,同時可以減少代碼的冗餘性。

繼承機制的使用可以複用一些定義好的類,減少重複代碼的編寫。多態機制可以動態調整對象的調用,降低對象之間的依存關係。

9.1 概念

9.1.1 繼承的概念

首先我們先了解一下類的封裝:
封裝將類的某些信息隱藏在類內部,不允許外部程序直接訪問,只能通過該類提供的方法來實現對隱藏信息的操作和訪問。例如:一臺計算機內部極其複雜,有主板、CPU、硬盤和內存, 而一般用戶不需要了解它的內部細節,不需要知道主板的型號、CPU 主頻、硬盤和內存的大小,於是計算機製造商將用機箱把計算機封裝起來,對外提供了一些接口,如鼠標、鍵盤和顯示器等,這樣當用戶使用計算機就非常方便。

封裝的特點:

  1. 只能通過規定的方法訪問數據。
  2. 隱藏類的實例細節,方便修改和實現。

繼承

概念:繼承是面向對象的三大特徵之一。繼承和現實生活中的“繼承”的相似之處是保留一些父輩的特性,從而減少代碼冗餘,提高程序運行效率。Java 中的繼承就是在已經存在類的基礎上進行擴展,從而產生新的類。已經存在的類稱爲父類、基類或超類,而新產生的類稱爲子類或派生類。在子類中,不僅包含父類的屬性和方法,還可以增加新的屬性和方法。
格式

修飾符 class class_name extends extend_class {
    // 類的主體
}

Java 的繼承通過 extends 關鍵字來實現,extends 的英文意思是擴展,而不是繼承。extends 很好的體現了子類和父類的關係,即子類是對父類的擴展,子類是一種特殊的父類。從這個角度看,使用繼承來描述子類和父類的關係是錯誤的,用擴展更恰當。
與C++的區別

Java 與 C++ 定義繼承類的方式十分相似。Java 用關鍵字 extends 代替了 C++ 中的冒號(:)。
在 Java 中,所有的繼承都是公有繼承, 而沒有 C++ 中的私有繼承和保護繼承。

類的繼承不改變類成員的訪問權限,也就是說,如果父類的成員是公有的、被保護的或默認的,它的子類仍具有相應的這些特性,並且子類不能獲得父類的構造方法。

單繼承

Java 語言摒棄了 C++ 中難以理解的多繼承特徵,即 Java 不支持多繼承,只允許一個類直接繼承另一個類,即子類只能有一個直接父類,extends 關鍵字後面只能有一個類名。例如,如下代碼會導致編譯錯誤:

class Student extends Person,Person1,Person2{}
class Student extends Person,extends Person1,extends Person2{}

很多地方在介紹 Java 的單繼承時,可能會說 Java 類只能有一個父類,嚴格來講,這種說法是錯誤的,應該是一個類只能有一個直接父類,但是它可以有多個間接的父類。例如,Student 類繼承 Person 類,Person 類繼承 Person1 類,Person1 類繼承 Person2 類,那麼 Person1 和 Person2 類是 Student 類的間接父類。下圖展示了單繼承的關係。

圖 1  圖形類之間的關係

從圖中可以看出,三角形、四邊形和五邊形的直接父類是多邊形類,它們的間接父類是圖形類。圖形類、多邊形類和三角形、四邊形、五邊形類形成了一個繼承的分支。在這個分支上,位於下層的子類會繼承上層所有直接或間接父類的屬性和方法。如果兩個類不在同一個繼承樹分支上,就不會存在繼承關係,例如多邊形類和直線。

如果定義一個 Java 類時並未顯式指定這個類的直接父類,則這個類默認繼承 java.lang.Object 類。因此,java.lang.Object 類是所有類的父類,要麼是其直接父類,要麼是其間接父類。因此所有的 Java 對象都可調用 java.lang.Object 類所定義的實例方法。

使用繼承的注意點:

  1. 子類一般比父類包含更多的屬性和方法。
  2. 父類中的 private 成員在子類中是不可見的,因此在子類中不能直接使用它們。
  3. 父類和其子類間必須存在“是一個”即“is-a”的關係,否則不能用繼承。但也並不是所有符合“is-a”關係的都應該用繼承。例如,正方形是一個矩形,但不能讓正方形類來繼承矩形類,因爲正方形不能從矩形擴展得到任何東西。正確的繼承關係是正方形類繼承圖形類。
  4. Java 只允許單一繼承(即一個子類只能有一個直接父類,但是可以通過多態與接口實現類似多繼承),C++ 可以多重繼承(即一個子類有多個直接父類)。

繼承的優缺點
在面嚮對象語言中,繼承是必不可少的、非常優秀的語言機制,它有如下優點:

  1. 實現代碼共享,減少創建類的工作量,使子類可以擁有父類的方法和屬性。
  2. 提高代碼維護性和可重用性。
  3. 提高代碼的可擴展性,更好的實現父類的方法。

自然界的所有事物都是優點和缺點並存的,繼承的缺點如下:

  1. 繼承是侵入性的。只要繼承,就必須擁有父類的屬性和方法。
  2. 降低代碼靈活性。子類擁有父類的屬性和方法後多了些約束。
  3. 增強代碼耦合性(開發項目的原則爲高內聚低耦合)。當父類的常量、變量和方法被修改時,需要考慮子類的修改,有可能會導致大段的代碼需要重構。

9.1.2 多態的概念

多態性是面向對象編程的又一個重要特徵,它是指在父類中定義的屬性和方法被子類繼承之後,可以具有不同的數據類型或表現出不同的行爲,這使得同一個屬性或方法在父類及其各個子類中具有不同的含義。

對面向對象來說,多態分爲編譯時多態和運行時多態。其中編譯時多態是靜態的,主要是指方法的重載,它是根據參數列表的不同來區分不同的方法。通過編譯之後會變成兩個不同的方法,在運行時談不上多態。而運行時多態是動態的,它是通過動態綁定來實現的,也就是大家通常所說的多態性。

Java 實現多態有 3 個必要條件:繼承、重寫和向上轉型。只有滿足這 3 個條件,開發人員才能夠在同一個繼承結構中使用統一的邏輯實現代碼處理不同的對象,從而執行不同的行爲。

  1. 繼承:在多態中必須存在有繼承關係的子類和父類。
  2. 重寫:子類對父類中某些方法進行重新定義,在調用這些方法時就會調用子類的方法。
  3. 向上轉型:在多態中需要將子類的引用賦給父類對象,只有這樣該引用才既能可以調用父類的方法,又能調用子類的方法。

Demo
1)創建一個Animal類作爲父類

package Polymorphism;

public class Animal {
    public String name;
    public  int age;
    //構造方法
    Animal(String name, int age){
        this.age = age;
        this.name = name;
    }
    //用於重寫,多態的方法
    public void sayHello(){
        System.out.println("Animal say its age is "+this.age+" name is "+this.name);

    }
}

2)兩個子類Dog,Cat

package Polymorphism;

public class Cat extends Animal {
    private String hobby;
    Cat(String name, int age, String hobby){
        super(name,age);
        this.hobby = hobby;
    }
    public void sayHello(){
        System.out.println("Cat say:我的愛好是"+this.hobby);
    }
}

package Polymorphism;

public class Dog extends Animal {
    private String hobby;
    Dog(String name, int age, String hobby){
        super(name,age);
        this.hobby = hobby;
    }
    public void sayHello(){
        System.out.println("Dog say:我的愛好是"+this.hobby);
    }
}

3)測試類Test,體現多態

package Polymorphism;

/**
 * 需求:測試多態的條件:
 * 1.滿足繼承關係
 * 2.使用向上轉型
 * 3.方法重寫
 */

public class test_1 {
    public static void main(String []args){
        //創建父類Animal
        Animal an = new Animal("祖宗",223);

        //Animal
        an.sayHello();

        //cat
        an = new Cat("Tom",6,"catch mouse");//向上轉向,必須有繼承關係
        an.sayHello();//方法重寫

        //dog
        an = new Dog("Jimmy",8,"catch cat");
        an.sayHello();
    }
}

運行結果:
在這裏插入圖片描述

9.2 一些關鍵字

9.2.1 InstanceOf(雙目運算符)

嚴格來說 instanceof 是 Java 中的一個雙目運算符,由於它是由字母組成的,所以也是 Java 的保留關鍵字。在 Java 中可以使用 instanceof 關鍵字判斷一個對象是否爲一個類(或接口、抽象類、父類)的實例,語法格式如下所示。

boolean result = obj instanceof Class

其中,obj 是一個對象,Class 表示一個類或接口。obj 是 class 類(或接口)的實例或者子類實例時,結果 result 返回 true,否則返回 false。
Demo or Test

package Polymorphism;

import java.util.ArrayList;
import java.util.List;

/**
 * 目的:
 *  練習使用Instanceof來判斷類,接口,對象的實例
 * 運算符簡介:
 *  instanceof 是 Java 中的一個雙目運算符,由於它是由字母組成的,所以也是 Java 的保留關鍵字。
 *  在 Java 中可以使用 instanceof 關鍵字判斷一個對象是否爲一個類(或接口、抽象類、父類)的實例
 */

public class InstanceOf_Test {
    public static void main(String[]args){
        //創建一個對象
        Object oj = new Object();

        //判斷類型
        System.out.println( oj instanceof Object);
        
        //判斷繼承的類,這個時候的object是object定義的,沒有使用向上轉型
        System.out.println(oj instanceof InstanceOf_Test);//false

        //判斷接口
        ArrayList arrayList = new ArrayList();
        System.out.println(arrayList instanceof List);
        //反過來
      List array = new ArrayList();//這裏使用了向上轉型,true
        System.out.println( array instanceof ArrayList );

        //與null比
      //  Integer i = 1;
      //  System.out.println(i instanceof null);無法編譯,null與任何比較都是false

        //必須爲引用類型, 編譯器會檢查 obj 能否轉換成右邊的 class 類型,如果不能轉換則直接報錯,如果不能確定類型,則通過編譯。
        //int l = 9;
       //System.out.println(l instanceof  Integer);
        InstanceOf_Test in = new InstanceOf_Test();
        System.out.print(in.toString());//打印出類的信息
        //System.out.print(in instanceof String);//但是無法轉化爲String

    }
}


在這裏插入圖片描述
在這裏插入圖片描述
運行結果:
在這裏插入圖片描述

9.2.2 super and this

1.super
由於子類不能繼承父類的構造方法,因此,如果要調用父類的構造方法,可以使用 super 關鍵字。super 可以用來訪問父類的構造方法、普通方法和屬性。

super 關鍵字的功能:
在子類的構造方法中顯式的調用父類構造方法
訪問父類的成員方法和變量。

public class Person {
    public Person(String name) {//定義了有參構造函數就不會有默認缺省構造函數

    }
}
public class Student extends Person {

}

會發現 Student 類出現編譯錯誤,提示必須顯式定義構造方法,錯誤信息如下:

Implicit super constructor Person() is undefined for default constructor. Must define an explicit constructor

在本例中 JVM 默認給 Student 類加了一個無參構造方法,而在這個方法中默認調用了 super(),但是 Person 類中並不存在該構造方法,所以會編譯錯誤。

Demo
聲明父類 Person,類中定義兩個構造方法。示例代碼如下:

public class Person {
    public Person(String name, int age) {
    }
    public Person(String name, int age, String sex) {
    }
}

子類 Student 繼承了 Person 類,使用 super 語句來定義 Student 類的構造方法。示例代碼如下:

public class Student extends Person {
    public Student(String name, int age, String birth) {
        super(name, age); // 調用父類中含有2個參數的構造方法
    }
    public Student(String name, int age, String sex, String birth) {
        super(name, age, sex); // 調用父類中含有3個參數的構造方法
    }
}

super和this的區別

this 指的是當前對象的引用,super 是當前對象的父對象的引用。下面先簡單介紹一下 super 和 this 關鍵字的用法。

super 關鍵字的用法:

  1. super.父類屬性名:調用父類中的屬性
  2. super.父類方法名:調用父類中的方法
  3. super():調用父類的無參構造方法
  4. super(參數):調用父類的有參構造方法
如果構造方法的第一行代碼不是 this()super(),則系統會默認添加 super()

this 關鍵字的用法:

  1. this.屬性名:表示當前對象的屬性
  2. this.方法名(參數):表示調用當前對象的方法
  3. 當局部變量和成員變量發生衝突時,使用this.進行區分。

關於 Java super 和 this 關鍵字的異同,可簡單總結爲以下幾條。

  1. 子類和父類中變量或方法名稱相同時,用 super 關鍵字來訪問。可以理解爲 super 是指向自己父類對象的一個指針。在子類中調用父類的構造方法。this 是自身的一個對象,代表對象本身,可以理解爲 this 是指向對象本身的一個指針。在同一個類中調用其它方法。
  2. this 和 super 不能同時出現在一個構造方法裏面,因爲 this 必然會調用其它的構造方法,其它的構造方法中肯定會有 super 語句的存在,所以在同一個構造方法裏面有相同的語句,就失去了語句的意義,編譯器也不會通過。
  3. this( ) 和 super( ) 都指的是對象,所以,均不可以在 static 環境中使用,包括 static 變量、static 方法和 static 語句塊。
  4. 從本質上講,this 是一個指向對象本身的指針, 然而 super 是一個 Java 關鍵字。

9.3 類型轉化(存在繼承關係的類的轉換)

將一個類型強制轉換成另一個類型的過程被稱爲類型轉換。本節所說的對象類型轉換,是指存在繼承關係的對象,不是任意類型的對象。當對不存在繼承關係的對象進行強制類型轉換時,會拋出 Java 強制類型轉換(java.lang.ClassCastException)異常。

Java 中引用類型之間的類型轉換(前提是兩個類是父子關係)主要有兩種,分別是向上轉型(upcasting)和向下轉型(downcasting)。

9.3.1 向上轉型(用於多態體現)

向上轉型就是把子類對象直接賦給父類引用,不用強制轉換。使用向上轉型可以調用父類類型中的所有成員,不能調用子類類型中特有成員,最終運行效果看子類的具體實現。

9.3.2 向下轉型

與向上轉型相反,子類對象指向父類引用爲向下轉型,語法格式如下:

sonClass obj = (sonClass) fatherClass;

其中,fatherClass 是父類名稱,obj 是創建的對象,sonClass 是子類名稱。

向下轉型可以調用子類類型中所有的成員,不過需要注意的是如果父類引用對象指向的是子類對象,那麼在向下轉型的過程中是安全的,也就是編譯是不會出錯誤。但是如果父類引用對象是父類本身,那麼在向下轉型的過程中是不安全的,編譯不會出錯,但是運行時會出現我們開始提到的 Java 強制類型轉換異常,一般使用 instanceof 運算符來避免出此類錯誤。

例如,Animal 類表示動物類,該類對應的子類有 Dog 類,使用對象類型表示如下:

Animal animal = new Dog();    // 向上轉型,把Dog類型轉換爲Animal類型
Dog dog = (Dog) animal; // 向下轉型,把Animal類型轉換爲Dog類型,這裏的animal本身指向的是Dog類
Animal animal = new Animal();
Dog dog = (Animal) animal;//不安全,運行可能出錯

//
Animal animal = new Cat();//Animal animal = new Dog();
if (animal instanceof Cat) {//加一個判斷
    Cat cat = (Cat) animal; // 向下轉型
    ...
}

9.3.3 強制轉型

向下轉型就是強制轉型,向上轉型不必要使用強制轉型。
不具備繼承關係的類不能夠使用強制轉型。

Dog dog = new Dog();
Cat cat = (Cat)dog;    // 編譯出錯,不允許把Dog對象類型轉換爲Cat對象類型
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章