1. 程式人生 > >繼承、封裝、多型

繼承、封裝、多型

1.封裝        所謂封裝,就是將某些共同的東西提取出來成為一個抽象基類。例如:男人和女人都屬於人類,他們共同的行為都有吃、喝、住、行,所以我們將這些共同行為封裝在一個Human的基類,讓它擁有吃、喝、住、行的方法。

        封裝把一個物件的屬性私有化,同時提供一些可以被外界訪問的屬性的方法,如果不想被外界方法,我們大可不必提供方法給外界訪問。但是如果一個類沒有提供給外界訪問的方法,那麼這個類也沒有什麼意義了。

public abstract class Human{

    public abstract void eat();

    public abstract void drink();

    public abstract void live();

    public abstract void walk();

}

2.繼承         有了上面封裝好的Human基類,我們就可以建立一個Male和Female類,來表示男人和女人,讓Male和Female繼承於Human類,這樣Male類和Female類都擁有了Human的方法。Male和Female屬於Human的子類,而在Java中,並不支援多繼承,但可以過載多個介面(Interface)。 

public class Male extends Human {

    @Override     public void eat() {

    }

    @Override     public void drink() {

    }

    @Override     public void live() {

    }

    @Override     public void walk() {

    } }

public class Female extends Human {

    @Override     public void eat() {

    }

    @Override     public void drink() {

    }

    @Override     public void live() {

    }

    @Override     public void walk() {

    } }

         學習繼承一定少不了這三個東西:構造器、protected關鍵字、向上轉型。

  • 構造器

       通過前面我們知道子類可以繼承父類的屬性和方法,除了那些private的外還有一樣是子類繼承不了的---構造器。對於構造器而言,它只能夠被呼叫,而不能被繼承。 呼叫父類的構造方法我們使用super()即可。

     構建過程是從父類“向外”擴散的,也就是從父類開始向子類一級一級地完成構建。而且我們並沒有顯示的引用父類的構造器,這就是java的聰明之處:編譯器會預設給子類呼叫父類的構造器。但是,這個預設呼叫父類的構造器是有前提的:父類有預設構造器。如果父類沒有預設構造器,我們就要必須顯示的使用super()來呼叫父類構造器,否則編譯器會報錯:無法找到符合父類形式的構造器。

       對於子類而言, 其構造器的正確初始化是非常重要的,而且當且僅當只有一個方法可以保證這點:在構造器中呼叫父類構造器來完成初始化,而父類構造器具有執行父類初始化所需要的所有知識和能力。對於繼承而言,子類會預設呼叫父類的構造器,但是如果沒有預設的父類構造器,子類必須要顯示的指定父類的構造器,而且必須是在子類構造器中做的第一件事(第一行程式碼)。

  • protected關鍵字

       private訪問修飾符,對於封裝而言,是最好的選擇,但這個只是基於理想的世界,有時候我們需要這樣的需求:我們需要將某些事物儘可能地對這個世界隱藏,但是仍然允許子類的成員來訪問它們。這個時候就需要使用到protected。

       對於protected而言,它指明就類使用者而言,他是private,但是對於任何繼承與此類的子類而言或者其他任何位於同一個包的類而言,他卻是可以訪問的。

  • 向上轉型

在上面的繼承中我們談到繼承是is-a的相互關係,貓繼承與動物,所以我們可以說貓是動物,或者說貓是動物的一種。這樣將貓看做動物就是向上轉型。

       將子類轉換成父類,在繼承關係上面是向上移動的,所以一般稱之為向上轉型。由於向上轉型是從一個叫專用型別向較通用型別轉換,所以它總是安全的,唯一發生變化的可能就是屬性和方法的丟失。這就是為什麼編譯器在“未曾明確表示轉型”活“未曾指定特殊標記”的情況下,仍然允許向上轉型的原因。

3.多型        從上面我們可以看到,繼承了Human的Male和Female都分別要實現Human其中的四個方法。這是因為Human是抽象類(abstract關鍵字),並且其中的eat、drink、live、walk都是抽象方法,所以凡是繼承它的子類都要實現這四個抽象方法。         而多型的意思就是:雖是同一個動作eat,但Male在實現的時候可以是站著吃飯,而Female在實現的時候可以是坐著吃飯,同一個方法可以有不同的實現,這就是面向物件中的多型。 

3.1.Java實現多型有三個必要條件:繼承、重寫、向上轉型。

  •     繼承:在多型中必須存在有繼承關係的子類和父類。
  •     重寫:子類對父類中某些方法進行重新定義,在呼叫這些方法時就會呼叫子類的方法。
  •     向上轉型:在多型中需要將子類的引用賦給父類物件,只有這樣該引用才能夠具備技能呼叫父類的方法和子類的方法。

       只有滿足了上述三個條件,我們才能夠在同一個繼承結構中使用統一的邏輯實現程式碼處理不同的物件,從而達到執行不同的行為。

       對於Java而言,它多型的實現機制遵循一個原則:當超類物件引用變數引用子類物件時,被引用物件的型別而不是引用變數的型別決定了呼叫誰的成員方法,但是這個被呼叫的方法必須是在超類中定義過的,也就是說被子類覆蓋的方法。

3.2.多型實現形式:繼承和介面

  • 基於繼承實現的多型

        基於繼承的實現機制主要表現在父類和繼承該父類的一個或多個子類對某些方法的重寫,多個子類對同一方法的重寫可以表現出不同的行為。

       基於繼承實現的多型可以總結如下:對於引用子類的父類型別,在處理該引用時,它適用於繼承該父類的所有子類,子類物件的不同,對方法的實現也就不同,執行相同動作產生的行為也就不同。

       如果父類是抽象類,那麼子類必須要實現父類中所有的抽象方法,這樣該父類所有的子類一定存在統一的對外介面,但其內部的具體實現可以各異。這樣我們就可以使用頂層類提供的統一介面來處理該層次的方法。

  • 基於介面實現多型

        繼承是通過重寫父類的同一方法的幾個不同子類來體現的,那麼就可就是通過實現介面並覆蓋介面中同一方法的幾不同的類體現的。

        在介面的多型中,指向介面的引用必須是指定這實現了該介面的一個類的例項程式,在執行時,根據物件引用的實際型別來執行對應的方法。

       繼承都是單繼承,只能為一組相關的類提供一致的服務介面。但是介面可以是多繼承多實現,它能夠利用一組相關或者不相關的介面進行組合與擴充,能夠對外提供一致的服務介面。所以它相對於繼承來說有更好的靈活性。

  • 程式碼示例

public class A {       public String show(D obj) {           return ("A and D");       }          public String show(A obj) {           return ("A and A");       }       }      public class B extends A{       public String show(B obj){           return ("B and B");       }              public String show(A obj){           return ("B and A");       }    }      public class C extends B{      }      public class D extends B{      }      public class Test {       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));             }   }