日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

类对接口的实现不叫继承_Java多态的实现机制是什么,看完你就知道(值得收藏)...

發(fā)布時間:2025/3/20 java 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 类对接口的实现不叫继承_Java多态的实现机制是什么,看完你就知道(值得收藏)... 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

Java多態(tài)的實現(xiàn)機制是父類或接口定義的引用變量可以指向子類或實現(xiàn)類的實例對象,而程序調(diào)用的方法在運行期才動態(tài)綁定,就是引用變量所指向的具體實現(xiàn)對象的方法,也就是內(nèi)存里正在運行的那個對象的方法,而不是引用變量的類型中定義的方法。

淺談多態(tài)機制的意義及實現(xiàn)

在面向對象編程(Object-Oriented Programming, OOP)中,多態(tài)機制無疑是其最具特色的功能,甚至可以說,不運用多態(tài)的編程不能稱之為OOP。這也是為什么有人說,使用面向對象語言的編程和面向對象的編程是兩碼事。

多態(tài)并沒有一個嚴格的定義,維基百科上給它下的定義比較寬松:

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.

一、子類型和子類

這里我想先提一下子類型(Subtype)這個詞和子類(Subclass)的區(qū)別,簡單地說,只要是A類運用了extends關鍵字實現(xiàn)了對B類的繼承,那么我們就可以說Class A是Class B的子類,子類是一個語法層面上的詞,只要滿足繼承的語法,就存在子類關系。

子類型比子類有更嚴格的要求,它不僅要求有繼承的語法,同時要求如果存在子類對父類方法的改寫(override),那么改寫的內(nèi)容必須符合父類原本的語義,其被調(diào)用后的作用應該和父類實現(xiàn)的效果方向一致。

對二者的對比是想強調(diào)一點:只有保證子類都是子類型,多態(tài)才有意義。Java父類強制轉換子類原則。

二、多態(tài)的機制

本質(zhì)上多態(tài)分兩種:

  • 編譯時多態(tài)(又稱靜態(tài)多態(tài))
  • 運行時多態(tài)(又稱動態(tài)多態(tài))
  • 重載(overload)就是編譯時多態(tài)的一個例子,編譯時多態(tài)在編譯時就已經(jīng)確定,運行時運行的時候調(diào)用的是確定的方法。

    我們通常所說的多態(tài)指的都是運行時多態(tài),也就是編譯時不確定究竟調(diào)用哪個具體方法,一直延遲到運行時才能確定。這也是為什么有時候多態(tài)方法又被稱為延遲方法的原因。

    在維基百科中多態(tài)的行為被描述為:

    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.

    下面簡要介紹一下運行時多態(tài)(以下簡稱多態(tài))的機制。

    多態(tài)通常有兩種實現(xiàn)方法:

  • 子類繼承父類(extends)
  • 類實現(xiàn)接口(implements)
  • 無論是哪種方法,其核心之處就在于對父類方法的改寫或對接口方法的實現(xiàn),以取得在運行時不同的執(zhí)行效果。

    要使用多態(tài),在聲明對象時就應該遵循一條法則:聲明的總是父類類型或接口類型,創(chuàng)建的是實際類型。舉例來說,假設我們要創(chuàng)建一個ArrayList對象,聲明就應該采用這樣的語句:

    List list = newArrayList();

    而不是

    ArrayList list = newArrayList();

    在定義方法參數(shù)時也通常總是應該優(yōu)先使用父類類型或接口類型,例如某方法應該寫成:

    public void doSomething(List list);

    而不是

    public void doSomething(ArrayList list);

    這樣聲明最大的好處在于結構的靈活性:假如某一天我認為ArrayList的特性無法滿足我的要求,我希望能夠用LinkedList來代替它,那么只需要在對象創(chuàng)建的地方把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).

    虛擬機會在執(zhí)行程序時動態(tài)調(diào)用實際類的方法,它會通過一種名為動態(tài)綁定(又稱延遲綁定)的機制自動實現(xiàn),這個過程對程序員來說是透明的。

    三、多態(tài)的用途

    多態(tài)最大的用途我認為在于對設計和架構的復用,更進一步來說,《設計模式》中提倡的針對接口編程而不是針對實現(xiàn)編程就是充分利用多態(tài)的典型例子。

    定義功能和組件時定義接口,實現(xiàn)可以留到之后的流程中。同時一個接口可以有多個實現(xiàn),甚至于完全可以在一個設計中同時使用一個接口的多種實現(xiàn)(例如針對ArrayList和LinkedList不同的特性決定究竟采用哪種實現(xiàn))。

    四、多態(tài)的實現(xiàn)

    下面從虛擬機運行時的角度來簡要介紹多態(tài)的實現(xiàn)原理,這里以Java虛擬機(Java Virtual Machine, JVM)規(guī)范的實現(xiàn)為例。JVM 與 Linux 的內(nèi)存關系詳解。

    在JVM執(zhí)行Java字節(jié)碼時,類型信息被存放在方法區(qū)中,通常為了優(yōu)化對象調(diào)用方法的速度,方法區(qū)的類型信息中增加一個指針,該指針指向一張記錄該類方法入口的表(稱為方法表),表中的每一項都是指向相應方法的指針。

    方法表的構造如下:

    由于Java的單繼承機制,一個類只能繼承一個父類,而所有的類又都繼承自Object類。方法表中最先存放的是Object類的方法,接下來是該類的父類的方法,最后是該類本身的方法。這里關鍵的地方在于,如果子類改寫了父類的方法,那么子類和父類的那些同名方法共享一個方法表項,都被認作是父類的方法。

    注意這里只有非私有的實例方法才會出現(xiàn),并且靜態(tài)方法也不會出現(xiàn)在這里,原因很容易理解:靜態(tài)方法跟對象無關,可以將方法地址直接引用,而不像實例方法需要間接引用。

    更深入地講,靜態(tài)方法是由虛擬機指令invokestatic調(diào)用的,私有方法和構造函數(shù)則是由invokespecial指令調(diào)用,只有被invokevirtual和invokeinterface指令調(diào)用的方法才會在方法表中出現(xiàn)。

    由于以上方法的排列特性(Object——父類——子類),使得方法表的偏移量總是固定的。例如,對于任何類來說,其方法表中equals方法的偏移量總是一個定值,所有繼承某父類的子類的方法表中,其父類所定義的方法的偏移量也總是一個定值。

    前面說過,方法表中的表項都是指向該類對應方法的指針,這里就開始了多態(tài)的實現(xiàn):

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

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

    結合方法指針偏移量是固定的以及指針總是指向實際類的方法域,我們不難發(fā)現(xiàn)多態(tài)的機制就在這里:

    在調(diào)用方法時,實際上必須首先完成實例方法的符號引用解析,結果是該符號引用被解析為方法表的偏移量。

    虛擬機通過對象引用得到方法區(qū)中類型信息的入口,查詢類的方法表,當將子類對象聲明為父類類型時,形式上調(diào)用的是父類方法,此時虛擬機會從實際類的方法表(雖然聲明的是父類,但是實際上這里的類型信息中存放的是子類的信息)中查找該方法名對應的指針(這里用“查找”實際上是不合適的,前面提到過,方法的偏移量是固定的,所以只需根據(jù)偏移量就能獲得指針),進而就能指向實際類的方法了。

    我們的故事還沒有結束,事實上上面的過程僅僅是利用繼承實現(xiàn)多態(tài)的內(nèi)部機制,多態(tài)的另外一種實現(xiàn)方式:實現(xiàn)接口相比而言就更加復雜,原因在于,Java的單繼承保證了類的線性關系,而接口可以同時實現(xiàn)多個,這樣光憑偏移量就很難準確獲得方法的指針。所以在JVM中,多態(tài)的實例方法調(diào)用實際上有兩種指令:

    • invokevirtual指令用于調(diào)用聲明為類的方法;
    • invokeinterface指令用于調(diào)用聲明為接口的方法。

    當使用invokeinterface指令調(diào)用方法時,就不能采用固定偏移量的辦法,只能老老實實挨個找了(當然實際實現(xiàn)并不一定如此,JVM規(guī)范并沒有規(guī)定究竟如何實現(xiàn)這種查找,不同的JVM實現(xiàn)可以有不同的優(yōu)化算法來提高搜索效率)。

    我們不難看出,在性能上,調(diào)用接口引用的方法通常總是比調(diào)用類的引用的方法要慢。這也告訴我們,在類和接口之間優(yōu)先選擇接口作為設計并不總是正確的,當然設計問題不在本文探討的范圍之內(nèi),但顯然具體問題具體分析仍然不失為更好的選擇。

    個人見解:多態(tài)機制包括靜態(tài)多態(tài)(編譯時多態(tài))和動態(tài)多態(tài)(運行時多態(tài)),靜態(tài)多態(tài)比如說重載,動態(tài)多態(tài)是在編譯時不能確定調(diào)用哪個方法,得在運行時確定。動態(tài)多態(tài)的實現(xiàn)方法包括子類繼承父類和類實現(xiàn)接口。當多個子類上轉型(不知道這么說對不)時,對象掉用的是相應子類的方法,這種實現(xiàn)是與JVM有關的。

    end:如果你覺得本文對你有幫助的話,記得點贊轉發(fā),你的支持就是我更新動力。

    總結

    以上是生活随笔為你收集整理的类对接口的实现不叫继承_Java多态的实现机制是什么,看完你就知道(值得收藏)...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。