1. 程式人生 > >java多型總結以及動態繫結機制

java多型總結以及動態繫結機制

什麼是多型?
1.繼承體現了多型,JAVA裡沒有多繼承,一個類之能有一個父類。而繼承的表現就是多型。一個父類可以有多個子類,而在子類裡可以重寫父類的方法(例如方法print()),這樣每個子類裡重寫的程式碼不一樣,自然表現形式就不一樣。這樣用父類的變數去引用不同的子類,在呼叫這個相同的方法print()的時候得到的結果和表現形式就不一樣了,這就是多型,相同的訊息(也就是呼叫相同的方法)會有不同的結果。
例如A類被幾個子類繼承,子類都重寫了A類中的某個方法M,呼叫(A型別引用物件a).M的時候就會根據建立a的時候使用的是具體哪個子類而呼叫相應子類中的方法M,這就體現了程式的多型性。
在父類中宣告的方法,可以在子類中進行覆蓋(宣告為finial的除外),這樣,父類的引用在引用子類物件的時候可以做出不同的響應。
所以繼承中多型表現為:相同的訊息被髮送到子類或父類物件上,將導致完全不同的行為。多型允許將子類的物件當作父類的物件使用,某父型別的引用指向其子型別的物件,呼叫的方法是該子型別的方法。這裡引用和呼叫方法的程式碼編譯前就已經決定了,而引用所指向的物件可以在執行期間動態繫結。
2.介面體現了多型,介面一般是功能的封裝,正常一個介面可以有多個實現,根據不同的需求來編寫實現類,jvm會自動將實現了介面的類和介面繫結起來,雖然介面本身沒有實際意義,但是程式只需要呼叫介面即可呼叫實現該介面的特定類的方法,一個介面不同的實現類體現了多型。儘管介面的方法沒有實際意義,需要具體的類直接實現接口才可使用,需要什麼樣的直接實現即可,可插拔式,系統自動將介面和實現類繫結。呼叫的是介面中宣告的方法,而具體的功能是看你是用哪個實現來初始化這個物件的。
JAVA沒有多繼承,而介面實現了多繼承!一個類可以同時實現多個介面從而變相實現多繼承,例如
public class Son implements Father1,Father2,Father3{   
}
但是其缺點就是如果向一個java介面加入一個新的方法時,所有實現這個介面的類都得編寫具體的實現。


生活中的多型:
關於多型的例子不勝列舉。比方說按下 F1 鍵這個動作,如果當前在 Flash 介面下彈出的就是 AS 3 的幫助文件;如果當前在 Word 下彈出的就是 Word 幫助;在 Windows 下彈出的就是 Windows 幫助和支援。同一個事件發生在不同的物件上會產生不同的結果。


多型性的特徵:
傳送訊息給某個物件,讓該物件自行決定響應何種行為。 通過將子類物件引用賦值給超類物件引用變數來實現動態方法呼叫。
但是多型性未必一定要用介面,只要在繼承的基礎上存在方法的重寫、過載與動態連線即可體現多型性(如存在繼承關係的類之間)
java 的這種機制遵循一個原則:當超類物件引用變數引用子類物件時,被引用物件的型別而不是引用變數的型別決定了呼叫誰的成員方法,但是這個被呼叫的方法必須是在超類中定義過的,也就是說被子類覆蓋的方法。
如果a是類A的一個引用,那麼,a可以指向類A的一個例項,或者說指向類A的一個子類。
如果a是介面A的一個引用,那麼,a必須指向實現了介面A的一個類的例項。但是jvm會自動將介面引用和實現類繫結。


多型存在的三個必要條件
一、要有繼承;
二、要有重寫;
三、父類引用指向子類物件。



實現多型的技術支援:動態繫結,是指在執行期間判斷所引用物件的實際型別,根據其實際的型別呼叫其相應的方法,下面具體介紹。

程式繫結的概念:
繫結指的是一個方法的呼叫與方法所在的類(方法主體)關聯起來。對java來說,繫結分為靜態繫結和動態繫結;或者叫做前期繫結(編譯是繫結)和後期繫結(執行是繫結)。
也可以理解為該方法和所屬於類是否繫結的關係。

靜態繫結:
在程式執行前方法已經被繫結,此時由編譯器或其它連線程式實現。例如:C。
針對java簡單的可以理解為程式編譯期的繫結;這裡特別說明一點,java當中的方法只有final,static,private和構造方法是前期繫結
 
動態繫結:
後期繫結:在執行時根據具體物件的型別進行繫結。
若一種語言實現了後期繫結,同時必須提供一些機制,可在執行期間判斷物件的型別,並分別呼叫適當的方法。也就是說,編譯器此時依然不知道物件的型別,但方法呼叫機制能自己去調查,找到正確的方法主體。不同的語言對後期繫結的實現方法是有所區別的。但我們至少可以這樣認為:它們都要在物件中安插某些特殊型別的資訊。
動態繫結的過程:
虛擬機器提取物件的實際型別的方法表;
虛擬機器搜尋方法簽名;
呼叫方法。
 
關於繫結相關的總結:
在瞭解了三者的概念之後,很明顯我們發現java屬於後期繫結。在java中,幾乎所有的方法都是後期繫結的,在執行時動態繫結方法屬於子類還是基類。但是也有特殊,針對static方法和final方法由於不能被繼承,因此在編譯時就可以確定他們的值,他們是屬於前期繫結的。特別說明的一點是,private宣告的方法和成員變數不能被子類繼承,也就是該方法只屬於本類,所以方法和類綁定了,所有的private方法都被隱式的指定為final的(由此我們也可以知道:將方法宣告為final型別的一是為了防止方法被覆蓋,二是為了有效的關閉java中的動態繫結)。java中的後期繫結是有JVM來實現的,我們不用去顯式的宣告它,而C++則不同,必須明確的宣告某個方法具備後期繫結。
 
總結:對於java當中的方法而言,除了final,static,private和構造方法是前期繫結外,其他的方法全部為動態繫結。而動態繫結的典型發生在父類和子類的轉換宣告之下:jvm在編譯類時候,發現如果方法是final,static,private修飾或者構造方法,則判斷為靜態繫結,是屬於類的,它們只可以被該類呼叫,那麼此時這些方法的方法本體可以確定。否則為動態繫結,也就是此時無法確定這些方法可以被誰呼叫,只有在方法被呼叫的語句出現時候,再去根據物件引用所指實際物件去確定呼叫特定類的方法。例如若該方法被父類物件呼叫,同時該方法在子類中被重寫,那麼最終就會呼叫子類覆蓋的方法體。
JAVA 虛擬機器呼叫一個類方法時(靜態方法),它會基於物件引用的型別(通常在編譯時可知)來選擇所呼叫的方法。相反,當虛擬機器呼叫一個例項方法時,它會基於物件實際的型別(只能在執行時得知)來選擇所呼叫的方法,這就是動態繫結,是多型的一種。當程式執行並且使用動態繫結呼叫方法時,虛擬機器必須呼叫同x所指向的物件的實際型別相匹配的方法版本。動態繫結為解決實際的業務問題提供了很大的靈活性,是一種非常優美的機制。

注意:
與方法不同,在處理java類中的成員變數(例項變數和類變數)時,並不是採用執行時繫結,而是一般意義上的靜態繫結。所以在向上轉型的情況下,物件的方法可以找到子類,而物件的屬性(成員變數)還是父類的屬性(子類對父類成員變數的隱藏)


理解了“向上轉型”和“型別還原”便於理解多型性,此處不再贅述。
本人csdn轉載連結:http://blog.csdn.net/yuliangliang092/article/details/51948283

一個經典的多型題目:

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...{}  


問題:以下輸出結果是什麼?
A a1 = new A();  
        A a2 = new B();  
        B b = new B();  
        C c = new C();   
        D d = new D();   
        System.out.println(a1.show(b));   ①  
        System.out.println(a1.show(c));   ②  
        System.out.println(a1.show(d));   ③  
        System.out.println(a2.show(b));   ④  
        System.out.println(a2.show(c));   ⑤  
        System.out.println(a2.show(d));   ⑥  
        System.out.println(b.show(b));    ⑦  
        System.out.println(b.show(c));    ⑧  
        System.out.println(b.show(d));    ⑨     


 答案


①   A and A
②   A and A
③   A and D
④   B and A
⑤   B and A
⑥   A and D
⑦   B and B
⑧   B and B
⑨   A and D

解析:
1.例項物件為A,引數為物件B,B為A的子類。執行A.class中show(A obj)
2.同上
3.例項物件為A,引數為物件D,執行A.class中show(D obj)
4.例項物件依然為A,引數為B,本應執行A.class中show(A obj),但是,B.class重寫了show(A obj),所以執行B.class show(A obj)
5.同上
6.執行A.class show(D obj) B中並沒有重寫。
7,8.例項物件為B,引數為B或者B的子類,執行show(B obj)
9.例項物件為B,引數為D,因為B繼承自A,也可以執行A中的show(D obj)