Java編程思想——細話Java多態

多態是一項讓程序員”將改變的事物與未變的事物分離開來“的技術。它可以消除類型之間的耦合關係,爲程序員帶來更快的程序開發過程,更好的代碼組織,更好擴展的程序以及更容易的代碼維護。


一、什麼是多態

       多態不但可以改善代碼的組織結構和可讀性,還能夠創建可擴展的程序(即無論在項目最初創建時還是在需要添加新功能時都可以”生長“的程序)。之所以可以進行擴展,是因爲在多態中默認忘記了對象類型,比如類Shap,無論是Circle(圓形)、Square(正方形)還是Triangle(三角形)都可以說是一種Shape,也就是繼承中常提到的”is-a“關係,此時如果繼續添加一個Rhombus(菱形)類仍然滿足這種關係,所以當進行向上轉型時都可以說是一個Shape類,即忽視了具體類型,多態實例如下:

class Shape{
   public draw(){}
   public clear(){}
}
class Trangle extends Shape{
   //重寫父類方法
   public draw(){
      System.out.print("Draw Trangle");
   }
   public clear(){
     System.out.print("Clear Trangle");
   }
}
class Circle extends Shape{
   //重寫父類方法
   public draw(){
     System.out.print("Draw Circle ");
   }
   public clear(){
     System.out.print("Clear Circle ");
   }
}
//可以進行擴展Square、Rhombus……
public class Test{
   public static void main(String[] args){
     Shape[] shape = {new Trangle(),new Circle()};//多態,忽視Trangle和Circle的具體類型而一併看作爲Shape類型
     for(Shape s:shape){
        s.draw();
        s.clear();
     }
  }
}
out:
Draw Trangle
Clear Trangle
Draw Circle
Clear Circle

二、實現機制——動態綁定

       從上面的例子中我們可以看到當引用s調用draw()和clear()方法時並不是調用基類的方法,而是調用Trangle和Circle中各自重寫的方法,這正是我們想要的,但是它是如何實現的呢?實際上編譯器是無法得知的,這歸功於方法調用時的綁定

1.什麼是綁定

   將一個方法調用同一個方法主體關聯起來被稱爲綁定——即將Trangle和其重寫基類的方法進行關聯。

2.分類

   綁定分爲前期綁定和後期綁定:

  • 前期綁定是指程序執行前進行綁定,由編譯器和連接程序實現。(這是一種面向過程語言中的默認綁定方式
  • 後期綁定是指在運行時根據對象的類型進行綁定,所以也被稱爲動態綁定或運行時綁定

      要想進行後期綁定就必須要有某種機制來觸發,而這種機制就是方法調用機制,實際上編譯器是一直不知道對象類型的,但是方法調用機制能找到正確的方法體,並加以調用。現實當中,Java語言中除了static和final(包括private)方法之外,其他方法都是後期綁定,也就是說在Java中後期綁定是自動發生的。

3.爲什麼Java中的static、final、private修飾的方法都不是後期綁定

  • static方法——靜態方法本屬於類,所以其從創建後就與類關聯。
  • final方法——此類方法是不能就行重寫的,所以在調用過程中不會產生選擇的歧義,所以不需要後期綁定。
  • private方法——此類方法實際上是屬於final方法的,所以理由與final一致。

三、多態的缺陷

多態對於私有方法、域和靜態方法是不起作用的,原因如下:

  • 私有方法:對於private方法實際是final類型的方法,所以其不能被覆蓋,因此只能調用父類的私有方法。
  • :排除父類與子類同名域易混淆這一點不談,實際上倆個域是擁有倆塊獨立內存的,因此域不存在可以被覆蓋的問題,且域的訪問是由編譯器解析的,既然是編譯器解析,所以不會是動態綁定,因爲動態綁定是在執行時進行的,所以不是多態。
  • 靜態方法:多態實際就是一種對象類型的向上轉型,即將父類引用指向了子類對象,但是類本身不變,靜態方法實際是與類直接關聯的,而非單個的對象,所以靜態方法不具有多態性

**補充一點**:

      既然有向上轉型肯定就有向下轉型,但是由於子類的方法不只包含所有的基類方法,還可能擁有基類沒有的方法,所以在向下轉型時很可能出現丟失方法的情況以致於轉型失敗,具體實例如下:

class Useful{
   public void f(){}
   public void g(){}
}
class MoreUserful extends Useful{
   public void f(){}//重寫父類方法
   public void g(){}//重寫父類方法
   public void u(){}//子類包含基類沒有的方法
}
public class Test{
   public static void main(String[] args){
      Useful[] u={new Useful(),new MoreUseful()};  //多態(包含向上轉型)
      u[0].f();
      u[1].g();
      //若要通過父類調用子類方法,需要向下轉型
      (MoreUseful)u[0].u();//報錯ClassCastException異常
      (MoreUseful)u[1].u();//向下轉型成功!
   }
}

Java中所有轉型都會進行檢查,包括基本類型的強轉。——運行時類型識別(RTTI)

多態中會遇到的異常——ClassCastException類轉型異常 ,通常在向下轉型時失敗 。

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