Java面向對象特性—多態

多態

  1. 什麼是多態:對象的多種形態,一個對象被多種稱謂
  2. 表現方面:
  • 引用多態(繼承或接口實現)
    // 父類的引用可以指向本類的對象;
    Animal obj1 = new Animal();
    // 父類的引用可以指向子類的對象;
    Animal obj2 = new Dog();  
    

    這裏我們必須深刻理解引用多態的意義,才能更好記憶這種多態的特性。爲什麼子類的引用不能用來指向父類的對象呢?通俗的講:就以上面的例子來說,我們能說“狗是一種動物”,但是不能說“動物是一種狗”,狗和動物是父類和子類的繼承關係,它們的從屬是不能顛倒的。當父類的引用指向子類的對象時,該對象將只是看成一種特殊的父類(裏面有重寫的方法和屬性),反之,一個子類的引用來指向父類的對象是不可行的!!

  • 方法多態
    根據上述創建的兩個對象:本類對象和子類對象,同樣都是父類的引用,當我們指向不同的對象時,它們調用的方法也是多態的。
    (1)創建本類對象時,調用的方法爲本類方法;
    (2)創建子類對象時,調用的方法爲子類重寫的方法或者繼承的方法;
    (3)使用多態的時候要注意:如果我們在子類中編寫一個獨有的方法(沒有繼承父類的方法),此時就不能通過父類的引用創建的子類對象來調用該方法!!!
    注意: 繼承(包括接口的實現)是多態的基礎。
  1. 多態中方法調用和執行
  • 編譯時,方法調用看變量:父類引用指向不同的子類對象,所調用的方法隨子類變化而變化。
  • 運行時,方法執行看對象:如果一個子類型的對象,向上造型爲父類型的變量時,向上造型爲不同父類型變量可用的功能也是不一樣的。
  1. 爲什麼使用多態?(增強代碼的複用能力)
    class Person {
        int age;
    
        public Person(int age) {
            super();
            this.age = age;
        }
    
        public int getAge() {
            return age;
        }
    }
    
    public class Student extends Person {
    
        public Student(int age) {
            super(age);
        }
    
        public void learn() {
            System.out.println("好好學習,天天向上!");
        }
    }
    
    class Teacher extends Person {
    
        public Teacher(int age) {
            super(age);
        }
    
        public void teach() {
            System.out.println("好好教書!");
        }
    }
    
    class CalcBirthYear {
        public int CalcYear(Person person) {
            return 2019 - person.getAge();
        }
    }
    
    class Test {
        public static void main(String[] args) {
            Student student = new Student(20);
            Teacher teacher = new Teacher(30);
    
            CalcBirthYear calcBirthYear = new CalcBirthYear();
            int syear = calcBirthYear.CalcYear(student);
            int tyear = calcBirthYear.CalcYear(teacher);
            System.out.println("學生出生年:" + syear);
            System.out.println("老師出生年:" + tyear);
        }
    }
    

引用類型轉換

  1. 向上類型轉換(隱式/自動類型轉換,小類型轉換爲大類型)
  • 父類應用指向子類對象時就是向上類型轉換
    Dog dog = new Dog();
    Animal animal = dog;// 自動類型提升,向上類型轉換
    
  1. 向下類型轉換(強制類型轉換,大類型轉換爲小類型)
  • 將上述代碼再加上一行,我們再次將父類轉換爲子類引用,那麼會出現錯誤,編譯器不允許我們直接這麼做,雖然我們知道這個父類引用指向的就是子類對象,但是編譯器認爲這種轉換是存在風險的。如:
    Dog dog = new Dog();
    Animal animal = dog;// 自動類型提升,向上類型轉換
    Dog dog2 = animal;// 編譯報錯
    
  • 我們可以通過強制類型轉換。如:
    Dog dog = new Dog();
    Animal animal = dog;// 自動類型提升,向上類型轉換
    Dog dog2 = (Dog)animal;// 向下類型轉換,強制類型轉換
    
  • 但是如果父類引用沒有指向該子類的對象,則不能向下類型轉換,雖然編譯器不會報錯,但是運行的時候程序會出錯,如:
    Dog dog = (Dog)new Animal();// 編譯無錯,執行報錯!
    // 其實這就是上面所說的子類的引用指向父類的對象,而強制轉換類型也不能轉換!!
    
  • 還有一種情況是父類的引用指向其他子類的對象,則不能通過強制轉爲該子類的對象。如:
    Dog dog = new Dog();
    Animal animal = dog;// 自動類型提升,向上類型轉換
    Dog dog2 = (Dog)animal;// 向下類型轉換,強制類型轉換
    Cat cat = (Cat)animal;// 編譯無錯,執行報錯!
    

    這是因爲我們在編譯的時候進行了強制類型轉換,編譯時的類型是我們強制轉換的類型,所以編譯器不會報錯,而當我們運行的時候,程序給animal開闢的是Dog類型的內存空間,這與Cat類型內存空間不匹配,所以無法正常轉換。這兩種情況出錯的本質是一樣的,所以我們在使用強制類型轉換的時候要特別注意這兩種錯誤!!

  • 下面有個更安全的方式來實現向下類型轉換( instanceof運算符)。
    instanceof是Java的一個二元操作符,和==,>,<是同一類。由於它是由字母組成的,所以也是Java的保留關鍵字。它的作用是測試它左邊的對象是否是它右邊的類的實例,返回boolean類型數據。
    /*
    1)在強制類型轉換中,爲了避免出現ClassCastException,可以通過instanceof關鍵字判斷某個引用指向的對象是否爲指定類型
    2)語法:對象 instanceof 目標類型
    3)返回值:
      - true:說明對象就是目標類型或者目標類型的小子類型,可以轉換;
      - false:說明不滿足強制轉換前提,不能轉換;  
    */
    
    //利用if語句和instanceof運算符來判斷兩個對象的類型是否一致。
    if(animal in instanceof Cat) {
      Cat cat = (Cat)animal;
    }
    
  • 補充說明:在比較一個對象是否和另一個對象屬於同一個類實例的時候,我們通常可以採用instanceof和getClass兩種方法通過兩者是否相等來判斷,但是兩者在判斷上面是有差別的。Instanceof進行類型檢查規則是:你屬於該類嗎?或者你屬於該類的派生類嗎?而通過getClass獲得類型信息採用==來進行檢查是否相等的操作是嚴格的判斷,不會存在繼承方面的考慮;

總結:在寫程序的時候,如果要進行類型轉換,我們最好使用instanceof運算符來判斷它左邊的對象是否是它右邊的類的實例,再進行強制轉換。

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