Java 面向對象特性之多態性

多態性

面向對象(OOP)三大特性:封裝、繼承、多態。java

多態性(polymorphism)指同一行爲具備多種不一樣表現形式,在面向對象程序設計中表現爲同一消息能夠根據發送對象的類型不一樣,作出多種不一樣的行爲。bash

多態性的優勢ide

多態性可以從必定程度上消除類型之間的耦合關係,經過統一接口方式,不一樣類的對象能夠直接替換,程序更加靈活,可擴展。函數

面向對象多態性存在的三個必要條件性能

  • 繼承
  • 重寫
  • 父類引用指向子類對象

多態性的實現方式

重寫(Override)與重載(Overload)

靜態多態性:方法重載(Method Overloading)

方法重載(Method Overloading)容許類具備多個相同名稱的方法,可是方法參數列表不一樣。spa

重載形式:設計

case 1: 參數數量變化(有效)code

add(int, int)
add(int, int, int)

case 2: 參數數據類型變化(有效)對象

add(int, int)
add(int, float)

case 3: 參數數據類型順序變化(有效)blog

add(int, float)
add(float, int)

bad case 1: 僅改變返回類型(無效)

int add(int, int)
float add(int, int)

Java 方法簽名由方法名稱和其後的參數列表共同決定,僅改變返回類型編譯器沒法重載。 不過方法重載(Method Overloading)容許改變返回類型和存取權限,但二者不屬於方法簽名。

方法重載(Method Overloading)式多態性,即方法調用取決於調用時傳遞的參數(數量、類型、順序),屬於編譯時靜態多態性。

動態多態性:方法重寫(Method Overriding)

方法重寫(Method Overriding)容許子類對父類能夠訪問的方法,實現自定義行爲,可是方法簽名須要保持一致。重寫的優勢在於,無需修改父類代碼便可改變子類繼承的方法。

重寫形式:
重寫依賴繼承,經過父類引用,指向子類對象實現動態多態性。

public class Animal{
   public void sound(){
      System.out.println("Animal is making a sound");   
   }
}

public class Cat extends Animal{
    @Override
    public void sound(){
        System.out.println("Meow");
    }
    
    public static void main(String args[]){
        Animal obj = new Cat();
        obj.sound();
    }
}

輸出:

Meow

重寫(覆蓋)規則:

  • 方法簽名(方法名稱和參數列表)必須同樣,返回類型須要兼容。
  • 不能下降方法的存取權限。
  • static, private, final 標記的方法以及類的構造方法不能被重寫(覆蓋)。

分析緣由:
Java 經過方法簽名標識方法,所以重寫須要確保是子類繼承自父類的同一方法。

子類不能夠下降父類方法的存取權限(可見性),但能夠升級。繼承反映一種 「is a」 關係,子類是父類,支持父類全部對外開放的行爲。下降方法的存取權限,使得父類做爲統一接口方式調用方法的能力被破壞。

private, final 標記的方法以及父類的構造方法沒法繼承,故沒法重寫。

static 標記的方法爲靜態方法屬於類,經過類名.方法名形式調用,無需依賴對象。
靜態方法和屬性會被子類繼承,子類一樣容許定義同名靜態方法和屬性,區別於實例方法「重寫」和屬性「重名」,這種狀況被稱爲「隱藏」。此時子類中調用同名的父類靜態方法和屬性,須要指明父類名.方法名父類名.變量名

靜態綁定與動態綁定

多態性的類型能夠分爲運行時和編譯時,方法重寫(Method Overriding)表明運行時動態多態性,方法重載(Method Overloading)表明編譯時靜態多態性。

方法調用與方法體的關聯稱爲綁定,有兩種類型的綁定:在編譯時發生的靜態綁定(Static Binding or Early Binding)和在運行時發生的動態綁定(Dynamic Binding or Late Binding)。

static, private, final 標記的方法以及類的構造方法是靜態綁定的,在編譯時肯定所屬類的類型,所以這些方法沒法覆蓋。其餘非標記的方法能夠稱爲「虛函數」,Java 中其實並無「虛函數」的概念,全部普通函數(方法)默認都至關於 C++ 的」虛函數」容許覆蓋(Override),所以虛函數(Virtual Method)可以根據運行時具體對象的類型進行動態綁定實現動態多態性,例如方法重寫(Method Overriding)。

靜態綁定示例:

class Human{
   public static void walk()
   {
       System.out.println("Human walks");
   }
}
class Boy extends Human{
   public static void walk(){
       System.out.println("Boy walks");
   }
   public static void main( String args[]) {
       /* Reference is of Human type and object is
        * Boy type
        */
       Human obj = new Boy();
       /* Reference is of Human type and object is
        * of Human type.
        */
       Human obj2 = new Human();
       obj.walk();
       obj2.walk();
   }
}

輸出:

Human walks
Human walks

聲明爲 static 的方法不能被重寫,可是可以被再次聲明(隱藏)。

Static Binding vs Dynamic Binding

  • 靜態綁定發生在編譯時,而動態綁定發生在運行時。
  • 靜態綁定使用的是類信息:類的類型決定調用方法,而動態綁定使用的是對象信息:對象的類型決定調用方法。
  • 方法重載使用靜態綁定,而方法重寫使用動態綁定。

綜合練習

多態性示例程序:

class A {
    public String show(D obj) { // 方法一
        return ("A and D");
    }

    public String show(A obj) { // 方法二
        return ("A and A");
    }
}

class B extends A {
    public String show(B obj) { // 方法三
        return ("B and B");
    }

    public String show(A obj) { // 方法四
        return ("B and A");
    }
}

class C extends B {
}

class D extends B {
}

public class Main {

    public static void main(String[] args) {
        A a1 = new A();
        A a2 = new B();
        B b = new B();
        C c = new C();
        D d = new D();
        System.out.println("1--" + a1.show(b));
        System.out.println("2--" + a1.show(c));
        System.out.println("3--" + a1.show(d));
        System.out.println("4--" + a2.show(b));
        System.out.println("5--" + a2.show(c));
        System.out.println("6--" + a2.show(d));
        System.out.println("7--" + b.show(b));
        System.out.println("8--" + b.show(c));
        System.out.println("9--" + b.show(d));
    }
}

運行結果:

1--A and A
2--A and A
3--A and D
4--B and A
5--B and A
6--A and D
7--B and B
8--B and B
9--A and D

詳細分析:

A、B、C、D 各種繼承關係如圖所示:

類圖

  1. A a1 = new A(); 正常建立對象 a1,涉及函數重載 show(),a1 具備調用方法一 show(D obj) 和方法二 show(A obj) 的能力。
    a1.show(b) 由編譯器進行靜態綁定(前期綁定)方法二 show(A obj)。
  2. a1.show(c) 由編譯器進行靜態綁定(前期綁定)方法二 show(A obj)。
  3. a1.show(d) 由編譯器進行靜態綁定(前期綁定)方法一 show(D obj)。
  4. A a2 = new B(); 多態建立父類引用,指向子類對象,a2 向上轉型具備調用 A 類方法一 show(D obj) 和方法二 show(A obj) 的能力,其中子類 B 重寫父類 A 的方法二 show(A obj) 爲方法四 show(A obj)。記住向上轉型存在缺點,即不能調用子類中有,父類沒有的方法,如方法三 show(B obj)。
    a2.show(b) 運行時動態綁定(後期綁定)方法四 show(A obj)。
  5. a2.show(c) 運行時動態綁定(後期綁定)方法四 show(A obj)。
  6. a2.show(d) 由編譯器進行靜態綁定(前期綁定)方法一 show(D obj)。
  7. B b = new B(); 正常建立對象 b,涉及函數重載 show(),b 具備調用方法三 show(B obj) 和方法四 show(A obj) 的能力。同時 B 繼承自 A 所以擁有方法一 show(D obj) 和方法二 show(A obj) 其中方法二被方法四重寫覆蓋。
    b.show(b) 由編譯器進行靜態綁定(前期綁定)方法三 show(B obj)。
  8. b.show(c) 由編譯器進行靜態綁定(前期綁定)方法三 show(B obj)。
  9. b.show(d) 由編譯器進行靜態綁定(前期綁定)方法一 show(D obj)。
相關文章
相關標籤/搜索