Java 多態的實現機制

是父類或接口定義的引用變量能夠指向子類或實現類的實例對象,而程序調用的方法在運行期才動態綁定,就是引用變量所指向的具體實現對象的方法,也就是內存里正在運行的那個對象的方法,而不是引用變量的類型中定義的方法。程序員

淺談多態機制的意義及實現面試

在面向對象編程(Object-Oriented Programming, OOP)中,多態機制無疑是其最具特點的功能,甚至能夠說,不運用多態的編程不能稱之爲OOP。這也是爲何有人說,使用面嚮對象語言的編程和麪向對象的編程是兩碼事。算法

多態並無一個嚴格的定義,維基百科上給它下的定義比較寬鬆:編程

Subtype polymorphism, almost universally called just polymorphism in the context of object-oriented programming, is the ability of one type, A, to appear as and be used like another type, B.設計模式

1、子類型和子類架構

這裏我想先提一會兒類型(Subtype)這個詞和子類(Subclass)的區別,簡單地說,只要是A類運用了extends關鍵字實現了對B類的繼承,那麼咱們就能夠說Class A是Class B的子類,子類是一個語法層面上的詞,只要知足繼承的語法,就存在子類關係。app

子類型比子類有更嚴格的要求,它不只要求有繼承的語法,同時要求若是存在子類對父類方法的改寫(override),那麼改寫的內容必須符合父類本來的語義,其被調用後的做用應該和父類實現的效果方向一致。ide

對兩者的對比是想強調一點:只有保證子類都是子類型,多態纔有意義。函數

**2、多態的機制
**
本質上多態分兩種:性能

編譯時多態(又稱靜態多態)
運行時多態(又稱動態多態)

重載(overload)就是編譯時多態的一個例子,編譯時多態在編譯時就已經肯定,運行時運行的時候調用的是肯定的方法。

咱們一般所說的多態指的都是運行時多態,也就是編譯時不肯定究竟調用哪一個具體方法,一直延遲到運行時才能肯定。這也是爲何有時候多態方法又被稱爲延遲方法的緣由。

在維基百科中多態的行爲被描述爲:

The primary usage of polymorphism in industry (object-oriented programming theory) is the ability of objects belonging to different types to respond to method, field, or property calls of the same name, each one according to an appropriate type-specific behavior.

下面簡要介紹一下運行時多態(如下簡稱多態)的機制。

多態一般有兩種實現方法:

1.子類繼承父類(extends)
2.類實現接口(implements)

不管是哪一種方法,其核心之處就在於對父類方法的改寫或對接口方法的實現,以取得在運行時不一樣的執行效果。

要使用多態,在聲明對象時就應該遵循一條法則:聲明的老是父類類型或接口類型,建立的是實際類型。舉例來講,假設咱們要建立一個ArrayList對象,聲明就應該採用這樣的語句:

List list =newArrayList();

而不是

ArrayList list =newArrayList();

在定義方法參數時也一般老是應該優先使用父類類型或接口類型,例如某方法應該寫成:

publicvoid doSomething(List list);

而不是

publicvoid doSomething(ArrayList list);

這樣聲明最大的好處在於結構的靈活性:假如某一天我認爲ArrayList的特性沒法知足個人要求,我但願可以用LinkedList來代替它,那麼只須要在對象建立的地方把new ArrayList()改成new LinkedList便可,其它代碼一律不用改動。

The programmer (and the program) does not have to know the exact type of the object in advance, and so the exact behavior is determined at run-time (this is called late binding or dynamic binding).

虛擬機會在執行程序時動態調用實際類的方法,它會經過一種名爲動態綁定(又稱延遲綁定)的機制自動實現,這個過程對程序員來講是透明的。

3、多態的用途

多態最大的用途我認爲在於對設計和架構的複用,更進一步來講,《設計模式》中提倡的針對接口編程而不是針對實現編程就是充分利用多態的典型例子。定義功能和組件時定義接口,實現能夠留到以後的流程中。同時一個接口能夠有多個實現,甚至於徹底能夠在一個設計中同時使用一個接口的多種實現(例如針對ArrayList和LinkedList不一樣的特性決定究竟採用哪一種實現)。

4、多態的實現

下面從虛擬機運行時的角度來簡要介紹多態的實現原理,這裏以Java虛擬機(Java Virtual Machine, JVM)規範的實現爲例。

在JVM執行Java字節碼時,類型信息被存放在方法區中,一般爲了優化對象調用方法的速度,方法區的類型信息中增長一個指針,該指針指向一張記錄該類方法入口的表(稱爲方法表),表中的每一項都是指向相應方法的指針。

方法表的構造以下:

因爲Java的單繼承機制,一個類只能繼承一個父類,而全部的類又都繼承自Object類。方法表中最早存放的是Object類的方法,接下來是該類的父類的方法,最後是該類自己的方法。這裏關鍵的地方在於,若是子類改寫了父類的方法,那麼子類和父類的那些同名方法共享一個方法表項,都被認做是父類的方法。

注意這裏只有非私有的實例方法纔會出現,而且靜態方法也不會出如今這裏,緣由很容易理解:靜態方法跟對象無關,能夠將方法地址直接引用,而不像實例方法須要間接引用。

更深刻地講,靜態方法是由虛擬機指令invokestatic調用的,私有方法和構造函數則是由invokespecial指令調用,只有被invokevirtual和invokeinterface指令調用的方法纔會在方法表中出現。

因爲以上方法的排列特性(Object——父類——子類),使得方法表的偏移量老是固定的。例如,對於任何類來講,其方法表中equals方法的偏移量老是一個定值,全部繼承某父類的子類的方法表中,其父類所定義的方法的偏移量也老是一個定值。

前面說過,方法表中的表項都是指向該類對應方法的指針,這裏就開始了多態的實現:

假設Class A是Class B的子類,而且A改寫了B的方法method(),那麼在B的方法表中,method方法的指針指向的就是B的method方法入口。

而對於A來講,它的方法表中的method方法則會指向其自身的method方法而非其父類的(這在類加載器載入該類時已經保證,同時JVM會保證老是能從對象引用指向正確的類型信息)。

結合方法指針偏移量是固定的以及指針老是指向實際類的方法域,咱們不難發現多態的機制就在這裏:

在調用方法時,實際上必須首先完成實例方法的符號引用解析,結果是該符號引用被解析爲方法表的偏移量。虛擬機經過對象引用獲得方法區中類型信息的入口,查詢類的方法表,當將子類對象聲明爲父類類型時,形式上調用的是父類方法,此時虛擬機會從實際類的方法表(雖然聲明的是父類,可是實際上這裏的類型信息中存放的是子類的信息)中查找該方法名對應的指針(這裏用「查找」其實是不合適的,前面提到過,方法的偏移量是固定的,因此只需根據偏移量就能得到指針),進而就能指向實際類的方法了。

咱們的故事尚未結束,事實上上面的過程僅僅是利用繼承實現多態的內部機制,多態的另一種實現方式:實現接口相比而言就更加複雜,緣由在於,Java的單繼承保證了類的線性關係,而接口能夠同時實現多個,這樣光憑偏移量就很難準確得到方法的指針。因此在JVM中,多態的實例方法調用實際上有兩種指令:

  • invokevirtual指令用於調用聲明爲類的方法;
  • invokeinterface指令用於調用聲明爲接口的方法。

當使用invokeinterface指令調用方法時,就不能採用固定偏移量的辦法,只能老老實實挨個找了(固然實際實現並不必定如此,JVM規範並無規定究竟如何實現這種查找,不一樣的JVM實現能夠有不一樣的優化算法來提升搜索效率)。咱們不難看出,在性能上,調用接口引用的方法一般老是比調用類的引用的方法要慢。這也告訴咱們,在類和接口之間優先選擇接口做爲設計並不老是正確的,固然設計問題不在本文探討的範圍以內,但顯然具體問題具體分析仍然不失爲更好的選擇。

我的看法:多態機制包括靜態多態(編譯時多態)和動態多態(運行時多態),靜態多態好比說重載,動態多態是在編譯時不能肯定調用哪一個方法,得在運行時肯定。動態多態的實現方法包括子類繼承父類和類實現接口。當多個子類上轉型(不知道這麼說對不)時,對象掉用的是相應子類的方法,這種實現是與JVM有關的。

今天就分享這麼多,歡迎各位朋友在留言區評論,對於有價值的留言,我都會一一回復的。若是以爲文章對你有一丟丟幫助,請給我點個贊吧,讓更多人看到該文章。另外,小編最近將收集的Java程序員進階架構師和麪試的資料作了一些整理,免費分享給每一位學習Java的朋友,須要的能夠進羣:751827870,歡迎你們進羣和我一塊兒交流。

相關文章
相關標籤/搜索