1. 程式人生 > 其它 >學習筆記--Java面向物件的繼承

學習筆記--Java面向物件的繼承

Java面向物件的繼承,學習記錄18

Java面向物件的繼承

繼承

  • 繼承是面向物件的三大特性之一

  • 繼承基本作用:程式碼複用;重要作用:有了繼承才能有以後的“方法的覆蓋”和“多型”

  • 繼承語法格式:

      [修飾符列表] class 類名 extends 父類名{
          類體
      }
    
  • Java語言當中的繼承只支援單繼承

  • 術語:

    B類繼承A類,其中:

    • A類稱為:父類、基類、超類、superclass
    • B類稱為:子類、派生類、subclass
  • Java語言中子類繼承父類:

    • 私有的不支援
    • 構造方法不支援
    • 其他資料都可以被繼承
  • Java語言中假設沒有顯示的繼承任何類,預設繼承JavaSE庫提供的java.lang.Object類

public class ExtendsTest01 {

    public static void main(String[] args) {
        
        C c1 = new C();

        c1.doSome();    // 這裡呼叫的doSome方法是從B類中繼承的
    }
}

class A{
    public void doSome() {
        System.out.println("do some !");
    }
}

class B extends A{

}

class C extends B{

}

方法的覆蓋

【回顧】Java中的·方法過載:

  • 方法過載稱為Overload

  • 什麼時候能用

    在同一個類當中,方法完成功能相似,建議方法同名

  • 什麼樣條件構成方法過載

    • 在同一個類當中
    • 方法名相同
    • 引數列表不同:型別、順序、個數
  • 方法過載與什麼無關

    • 方法的返回值型別無關
    • 方法的修飾符列表無關

【正片】方法覆蓋

  • 方法覆蓋【override官方】又被稱作方法重寫【overwrite】

  • 什麼時候使用方法覆蓋

    當父類中的方法已經無法滿足當前子類的需求,子類有必要將父類中繼承的方法進行重新編寫,這個過程稱為方法重寫/方法覆蓋

  • 程式碼滿足什麼條件會觸發方法覆蓋?

    • 發生在具有繼承關係的父子類之間
    • 返回值型別相同,方法名相同,形引數列表相同
    • 訪問許可權不能更低,可以更高
    • 丟擲異常不能更多,可以更少
  • 方法重新建議複製過去改【防止寫錯】

  • 注意:

    • 私有方法不能繼承,所以不能覆蓋
    • 構造方法不能繼承,所以不能覆蓋
    • 靜態方法不存在覆蓋【後面多型詳講】
    • 覆蓋只針對方法,不談屬性
// 建立動物類
public class Animal {
    
    public void move() {
        System.out.println("動物在移動");
    }
}
// 建立貓類
public class Cat extends Animal{
    
    // 重新父類方法
    public void move() {
        System.out.println("貓在走貓步");
    }
}
// 建立鳥類
public class Bird extends Animal{
    
    // 重新父類方法
    public void move() {
        System.out.println("鳥在飛");
    }
}
public class OverrideTest01 {
    
    public static void main(String[] args) {
        
        // 建立動物物件
        Animal a = new Animal();
        a.move();

        // 建立貓物件
        Cat c = new Cat();
        c.move();

        // 建立鳥物件
        Bird b = new Bird();
        b.move();
    }
}

多型

Java中多型機制【基礎規則】

多型作用:

降低程式的耦合度【解耦合】,提高程式的可拓展性【儘量使用】

  • 面向物件三大特徵:封裝、繼承、多型

  • 關於多型中涉及的概念:

    • 向上轉型(upcasting)

      • 子型別 --> 父型別

        又被稱為:自動型別轉換

      • 通過執行一定不會出問題

    • 向下轉型(downcasting)

      • 父型別 --> 子型別

        又被稱為:強制型別轉換【需要加強制型別轉換符】

      • 通過執行可能出問題【著名異常:java.lang.ClassCastException】

    • 無論是向下轉型(upcasting)還是向上轉型(downcasting),兩種型別之間必須要有繼承關係。沒有繼承關係無法通過編譯

    • 當呼叫的方法是子型別特有,在父型別當中沒有。必須進行向下轉型(downcasting)

    • 在向下轉型(downcasting)中如何避免 ClassCastException 異常

      使用 instanceof 運算子

        (引用 instanceof 資料型別名)
      

      計算結果為 Boolean【布林型別】

      • true:表示這個引用指向的物件是此資料型別
      • false:表示這個引用指向的物件不是此資料型別
    • Java規範中要求:在使用強制型別轉換前,建議採用 instanceof 運算子判斷,避免 ClassCastException 異常

Animal、Cat、Bird三個類之間的關係:

  • Cat 繼承 Animal
  • Bird 繼承 Animal
  • Cat 與 Bird無繼承關係
// 建立動物類
public class Animal {
    
    public void move() {
        System.out.println("動物在移動");
    }
}
// 建立貓類
public class Cat extends Animal{
    
    // 重新父類方法
    public void move() {
        System.out.println("貓在走貓步");
    }

    // 不是從父類中繼承的方法
    // 這個方法是子類物件特有行為
    public void catchMouse() {
        System.out.println("貓抓老鼠");
    }
}
// 建立鳥類
public class Bird extends Animal{
    
    // 重新父類方法
    public void move() {
        System.out.println("鳥在飛");
    }

    // 子類物件特有行為
    public void fly() {
        System.out.println("Bird fly !");
    }
}
public class Test {
    
    public static void main(String[] args) {
        
        // 以前編寫的程式

        // 建立動物物件
        Animal a1 = new Animal();
        a1.move();

        // 建立貓物件
        Cat c1 = new Cat();
        c1.move();
        c1.catchMouse();

        // 建立鳥物件
        Bird b1 = new Bird();
        b1.move();

        // 使用動態機制
        System.out.println("-----以下使用動態機制----");

        Animal a2 = new Cat();  // 想象 long num = 10;
        // Cat is a Animal
        // new Cat()建立的物件型別是Cat,Animal a2中 a2 引用的資料型別是Animal,可見它們進行型別轉換
        // 子型別轉換父型別,稱為向上轉型(upcasting),或者自動型別轉換

        // Bird b2 = new Cat();     Bird與Cat無繼承關係
        

        // 編譯器只會把a2型別視作Animal
        // 執行時是看底層的實際內容
        a2.move();  // 貓在走貓步

        // a2.catchMouse();     報錯
        // 編譯器檢查為Animal型別,Animal中沒有catchMouse()方法,導致靜態繫結失敗


        // 需求:需要a2去執行catchMouse()方法
        // 可以通過向下轉型(downcasting),強制轉換,同樣需要兩者有繼承關係
        Cat c2 = (Cat)a2;
        c2.catchMouse();



        // 父型別引用指向子型別物件【多型】
        // 以下程式編譯無錯語法允許,但執行階段報錯JVM堆記憶體實際存在的是Bird物件
        // 型別之間不存在如何繼承關係,會發生著名異常:java.lang.ClassCastException
        // 觸發一般在向下轉型(downcasting),一定注意

        // Animal  a3 = new Bird();
        // Cat c3 = (Cat)a3;

        // c3.catchMouse(); 報錯    著名異常:java.lang.ClassCastException

        // 避免ClassCastException
        Animal  a3 = new Bird();

        if(a3 instanceof Cat) {
            Cat c3 = (Cat)a3;
            c3.catchMouse();
        }else if(a3 instanceof Bird){
            Bird b3 = (Bird)a3;
            b3.fly();
        }

    }
}

  1. java程式永遠都分為編譯階段和執行階段
  2. 先分析編譯階段,再分析執行階段,編譯無法通過,根本無法執行
  3. 編譯階段編譯器檢查a2這個引用的資料型別是Animal,由於Animal.class位元組碼當中有move()方法,所以編譯通過,這個過程我們稱為靜態繫結,編譯階段繫結。只有靜態繫結成功之後才有程式執行
  4. 在程式執行階段,JVM堆記憶體中真實建立的物件是Cat物件,那麼上述程式中 a2.move(); 一定呼叫Cat物件的move()方法【方法重寫無關】,稱為動態繫結,執行階段繫結
  5. 無論重寫與否,執行階段只與底層實際關聯

父型別引用指向子型別物件這種機制導致程式在編譯階段繫結和執行階段繫結兩種不同的形態,這種機制可以成為一種動態語法機制