1. 程式人生 > 其它 >【Java】面向物件之多型

【Java】面向物件之多型

技術標籤:JavaJava面向物件多型

多型

在設計一個方法時,通常希望該方法具備一定的通用性。例如要實現一個動物叫的方法,由於每種動物的叫聲是不同的,因此可以在方法中接收一個動物型別的引數,當傳入貓類物件時就發出貓類的叫聲,傳入犬類物件時就發出犬類的叫聲。在同一個方法中,這種由於引數型別不同而導致執行效果各異的現象就是多型。繼承是多型得以實現的基礎。

在Java中為了實現多型,允許使用一個父類型別的變數來引用一個子類型別的物件,根據被引用子類物件特徵的不同,得到不同的執行結果。

定義:某一類事物的多種存在形態。

例:動物中貓,狗。
貓這個物件對應的型別是貓型別:貓 x = new 貓();

同時貓也是動物中的一種,也可以把貓稱為動物:動物 y = new 貓();
動物是貓和狗具體事物中抽取出來的父型別。
父型別引用指向了子類物件。

多型性簡單說就是一個物件對應著不同型別。

體現:父類或者介面的引用指向或者接收自己的子類物件。
作用:多型的存在提高了程式的擴充套件性和後期可維護性。

前提:

  1. 需要存在繼承或者實現關係。
  2. 需要有覆蓋操作。

好處:提高了程式碼的擴充套件性,前期定義的程式碼可以使用後期的內容。
弊端:前期定義的內容不能使用(呼叫)後期子類的特有內容。

示例1:

 abstract class Animal{
        abstract void eat();
 }
class Dog extends Animal{ void eat(){ System.out.println("啃骨頭"); } void lookHome(){ System.out.println("看家"); } } class Cat extends Animal{ void eat(){ System.out.println("吃魚"); }
void catchMouse(){ System.out.println("抓老鼠"); } } class Pig extends Animal{ void eat(){ System.out.println("飼料"); } void gongdi(){ System.out.println("拱地"); } } class DuoTaiDemo{ public static void main(String[] args){ Cat c = new Cat(); Dog d = new Dog(); method(c); method(d); method(new Pig()); } public static void method(Animal a){ a.eat(); } }

執行結果:

在這裡插入圖片描述

示例2:

class DuoTaiDemo{
    public static void main(String[] args){
        //自動型別提升,貓物件提升到了動物型別。但是特有功能無法訪問,作用就是限制對特有功能
        的訪問。
        //專業講:向上轉型,將子型別隱藏。就不能使用子類的特有方法了。
        Animal a = new Cat();
        a.eat();
        //a.catchMouse();//報錯

        //如果還想用具體動物貓的特有功能。
        //你可以將該物件進行向下轉型。
        Cat c = (Cat)a; //向下轉型的目的是為了能夠使用子類中的特有方法。
        c.eat();
        c.catchMouse();

        //注意:對於轉型,自始至終都是子類物件在做型別的變化。
        //Animal a = new Dog();
        //Cat c = (Cat)a;//但是型別不能隨意轉換,否則可能會報出ClassCastException的異常
    }

    public static void method(Animal a){
        a.eat();
    }
}

執行結果:

在這裡插入圖片描述

示例3:

/*
 畢老師和畢姥爺的故事
 */
class 畢姥爺{
    void 講課(){
        System.out.println("管理");
    }
    void 釣魚(){
        System.out.println("釣魚");
    }
}

class 畢老師 extends 畢姥爺{
    void 講課(){
        System.out.println("Java");
    }
    void 看電影(){
        System.out.println("看電影");
    }
}

class DuoTaiDemo{
    public static void main(String[] args){
        畢老師 x = new 畢老師();
        x.講課(); //Java
        x.看電影(); //看電影

        畢姥爺 y = new 畢老師();
        y.講課(); //Java
        y.釣魚(); //釣魚

        畢老師 z = (畢老師)y;
        z.看電影(); //看電影
    }
}

執行結果:

在這裡插入圖片描述

instanceof :用於判斷物件的具體型別,只能用於引用資料型別判斷,通常在向下轉型前用於健壯性的判斷。

示例4:

class DuoTaiDemo{
    public static void main(String[] args){
    }

    public static void method(Animal a){
        a.eat();

        if(a instanceof Cat){
            Cat c = (Cat )a;
            c.catchMouse();
        }
        else if (a instanceof Dog){
            Dog d = (Dog )a;
            d.lookHome();
        }
    }
}

多型時,成員的特點:

  1. 成員變數

編譯時:參考引用型變數所屬的類中是否有呼叫的成員變數。有,編譯通過,沒有,編譯失敗。

執行時:參考引用型變數所屬的類中是否有呼叫的成員變數,並執行該所屬類中的成員變數。

簡單說:編譯和執行都參考等號的左邊。

示例:

class Fu{
    int num = 3;
}

class Zi extends Fu{
    int num = 4;
}

class DuoTaiDemo{
    public static void main(String[] args){
        Zi f1 = new Zi();
        System.out.println(f1.num);
        Fu f2 = new Zi();
        System.out.println(f2.num);
    }
}

執行結果:

在這裡插入圖片描述

  1. 成員函式(非靜態)

編譯時:參考引用型變數所屬的類中是否有呼叫的函式。有,編譯通過。沒有,編譯失敗。

執行時:參考的是物件所屬的類中是否有呼叫的函式。

簡單說:編譯看左邊,執行看右邊。

示例:

class Fu{
    void show(){
        System.out.println("fu show");
    }
}

class Zi extends Fu{
    void show(){
        System.out.println("zi show");
    }
}

class DuoTaiDemo{
    public static void main(String[] args){
        Fu f = new Zi();
        f.show();
    }
}

執行結果:

在這裡插入圖片描述

  1. 靜態函式

編譯時:參考的是物件所屬的類中是否有呼叫的函式。

執行時:參考的是物件所屬的類中是否有呼叫的函式。

簡單說:編譯和執行看左邊。

示例:

class Fu{
    static void method(){
        System.out.println("fu static method");
    }
}

class Zi extends Fu{
    static void method(){
        System.out.println("zi static method");
    }
}

class DuoTaiDemo{
    public static void main(String[] args){
        Fu f = new Zi();
        f.method();// fu static method
        Fu.method();
    }
}

執行結果:

在這裡插入圖片描述

java中實現多型的機制是什麼?

靠的是父類或介面定義的引用變數可以指向子類或具體實現類的例項物件,而程式呼叫的方法在執行期才動態繫結,就是引用變數所指向的具體例項物件的方法,也就是記憶體里正在執行的那個物件的方法,而不是引用變數的型別中定義的方法。重寫、過載是多型性的不同表現

父類A有一個方法function(),子類B,C分別繼承A並且重寫function(),當建立一個物件A b = new B();b.function()就呼叫B的funciotn,假如你new C(),那呼叫的就是C重寫的function。怎麼判斷使用那個類的function就是動態繫結,這個現象就是多型…

動態繫結機制