繼承、重寫和多態

繼承、重寫和多態

還是最經典的例子,Animal, Cat, Dog

繼承

關於繼承,先總結,再給出代碼示例

關於Java中的繼承:

  • 1 繼承繼承是面向對象三大特徵之一 (封裝、 繼承、 多態)
  • 2 繼承的基本作用:代碼複用,但是繼承最重要的作用是有了繼承纔有了 “重寫” 和 “多態機制”。
  • 3 繼承語法格式:
     class 類名 extends 父類名 {
          屬性 ;
          方法()
     } 
  • 4 java中只支持單繼承,一個類不能同時繼承多個類。
  • 5 一些基本術語:
     /**
      * A可以稱爲:父類、基類、超類、superclass 
      * B可以稱爲:子類、派生類、subclass  
      **/
     class A extends B {}        
  • 6 私有的不繼承

      構造方法不繼承

      其他數據均可繼承
  • 7 雖然java不支持多繼承,但可以間接繼承,例如:
     /**
      * A直接繼承B, A間接繼承C, D
      **/
     class A extends B {}
     class B extends C {}
     class C extends D {}
  • 8 所有類默認繼承java.lang.Object類

示例

public class Animal {
    public void move() {
        System.out.println("Animal is moving");
    }
}
public class Cat extends Animal {
    @override   // 重寫
    public void move() {
        System.out.println("cat is moving");
    }
}
public class Test {
    public static void main(String[] args) {
        Cat cat = new Cat();
        cat.move();
    }
}

運行結果:

cat is moving

(若Cat類中沒有重寫,則結果爲 Animal is moving)


重寫

  • 當父類方法無法滿足子類的業務需求時,子類有必要將父類中繼承的方法進行重寫。(例如上面的Animal和Cat)
  • 重寫需要滿足的條件:
      方法重寫發生在具有繼承關係的父子類之間
      返回值類型相同,方法名相同,形參列表相同
      訪問權限不能更低,拋出異常不能更多
  • 私有方法不能繼承,所以更不能重寫
  • 構造方法不能繼承,更不能重寫
  • 靜態方法不存在重寫
  • 重寫只針對方法,不談屬性



多態

public class Animal {
    public void move() {
        System.out.println("animal is moving");
    }
}
public class Cat extends Animal {

    // 重寫父類方法
    @Override
    public void move() {
        System.out.println("cat is moving");
    }

    // 不是從父類繼承的,子類特有方法
    public void catchMouse() {
        System.out.println("catch mouse");
    }
}
public class Dog extends Animal {
    @Override
    public void move() {
        System.out.println("dog is moving");
    }
}
/**
 * 關於多態:
 *   1 Animal,Cat,Dog之間的關係:
 *       Cat和Dog都繼承自Animal
 *       Cat和Dog之間沒有繼承關係
 *   2 關於多態的幾個概念:
 *       向上轉型(自動類型轉換): 子 --> 父
 *       向下轉型(強制類型轉換): 父 --> 子
 *       轉型時,兩者之間必須要有繼承關係
 *       沒有繼承關係,無法編譯通過
 */
public class Test {
    public static void main(String[] args) {

        Animal a = new Cat();

        /**
         * 1 編譯階段編譯器檢查"a"這個引用的數據類型爲Animal,由於Animal.class字節碼中有move()方法,
         *      所以編譯通過。這個過程稱爲靜態綁定,編譯階段綁定,只有靜態綁定成功之後纔有後續的運行。
         *
         * 2 程序運行階段,JVM堆內存當中真實創建的對象是Cat對象,則程序在運行時會調用Cat的move()方法,
         *      此時發生了程序的動態綁定,運行階段綁定。
         *
         * 3 注意:這裏 a 調用的move()方法是 Cat 的move()方法,從父類繼承而來,與是否重寫無關
         */
        a.move();

        /**
         * a.catchMouse()無法通過編譯,原因:
         * 1 Java程序分爲編譯階段和運行階段
         * 2 先分析編譯階段,再分析運行階段。編譯無法通過,程序無法運行
         * 3 編譯階段編譯器檢查"a"這個引用的數據類型爲Animal,不存在catchMouse()方法,
         *      導致靜態綁定失敗,編譯失敗,更別談運行了。
         */
        // a.catchMose();
    }
}

運行結果:

cat is moving


那麼該如何調用catchMose()方法呢?



強制類型轉換

  • 爲了使用子類特有的方法,我們需要進行強制類型轉換,同樣是上面的Animal和Cat
    public class Test {
        public static void main(String[] args) {
            Animal a2 = new Cat();
            Cat c = (Cat) a2;
            c.catchMouse;
      }
    }
    

運行結果:

catch mouse


  • 補充一個非常經典的異常 :java.lang.ClassCastException
    public class Test {
        public static void main(String[] args) {
            Aniaml a3 = new Dog();
            Cat c1 = (Cat) a3;
        }
    }
    
  • 以上程序在編譯時沒有錯誤,因爲編譯階段 a3 是一個Animal類型,可以強轉爲Cat
  • 但是運行時會報錯,因爲真實對象是Dog類型,與Cat之間不存在繼承關係
  • 運行結果:
    在這裏插入圖片描述
  • 以上異常只會出現在向下轉型時,向上轉型只要編譯通過就不會有問題

當然啦,這個異常我們也是有辦法應對的,那就是 instanceof

  • instanceof返回的是一個boolean類型的值
    public class Test {
        public static void main(String[] args) {
            Animal a3 = new Dog();
            if (a3 instanceof Cat) {      // a3是否爲Cat的實例
                Cat c = (Cat) a3;
                c.catchMouse();
            } else if (a3 instanceof Dog) {
                Dog d = (Dog) a3;
                d.move();
            }
        }
    }
    

運行結果:

dog is moving

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章