更新時間:2020-03-06 10:32:54 來源:動力節點 瀏覽2016次
對于java中多態概念的理解一直是面試常問的問題,所以今天花了一些時間好好地整理了一下,力求從java虛擬機的角度來分析和理解多態。
一、認識多態
1、方法調用
在Java中,方法調用有兩類,動態方法調用與靜態方法調用。
?。?)靜態方法調用是指對于類的靜態方法的調用方式,是在編譯時刻就已經確定好具體調用方法的情況,是靜態綁定的。
?。?)動態方法調用需要有方法調用所作用的對象,是在調用的時候才確定具體的調用方法,是動態綁定的。
我們這里所講的多態就是后者—動態方法調用。
2、多態概念
多態有兩種:類內部之間的多態和類之間的多態。我們先看一下標準的概念:
多態是面向對象編程語言的重要特性,它允許基類的指針或引用指向派生類的對象,而在具體訪問時實現方法的動態綁定
?。?)Java的方法重載(類內部之間的多態):就是在類中可以創建多個方法,它們具有相同的名字,但可具有不同的參數列表、返回值類型。我們舉個例子來解釋,就是一對夫婦生了多胞胎,多胞胎之間外觀相似,其實是不同的孩子。
?。?)Java的方法重寫(父類與子類之間的多態):子類可繼承父類中的方法,但有時子類并不想原封不動地繼承父類的方法,而是想作一定的修改,這就需要采用方法的重寫。重寫的參數列表和返回類型均不可修改。我們再舉個例子,就是子承父業,但是兒子有自己想法,對父親得產業進行再投資的過程。
二、代碼實現多態
1、類內部之間得多態:方法重載
從上述代碼我們可以看到,在類的內部可以有相同的方法名,但是有唯一的參數列表。當然返回類型和修飾符也可以不同。下面我們再看一下類之間的多態。
2、類之間的多態:方法重寫
類之間的多態其實是有兩種方式:繼承和接口。我們對這兩種方式一個一個說明。
(1)繼承方式實現多態
對于繼承方式我們使用一個例子來解釋,比如說父親可以對自己的房子有處理權,兒子繼承父業同樣也有處理權。
第一步:定義父類
第二步:定義子類(大兒子和小兒子)
第三步:測試
(2)接口方式實現多態
接口方式實現繼承方式其實跟上面一樣,只不過把父類變成了接口而已,其他內容只有微笑的變化,這里就不演示了,在這里只給出父接口的形式。
到了這基本上就對多態形式的代碼實現進行了演示,案例也比較簡單,但是這對我們理解多態的思想還不夠,我們最主要的還是從虛擬機的角度來分析一下。
三、分析多態
想要深入分析多態,我們需要弄清楚幾個問題。
1、jvm內存
在上面的代碼中我們其實已經看到了,不管是類內部之間實現的多態,還是類之間實現的多態,這些方法的名字其實都是一樣的,那我們的程序在運行的時候,底層虛擬機是如何去區分的呢(java虛擬機實現動態調用)?為此我們還是先從java虛擬機講起。
其實java虛擬機在執行java程序的時候,并不是直接運行的,他需要一個過程,我們使用一張圖來看下:
上面這張圖已經很清晰,也就是說,我們的java文件要想運行,需要通過java編譯器編譯成.class文件,然后通過類裝載器講.class文件裝載到JVM中,最后才是執行。而且JVM分了五個區域,那么在代碼中定義的那些多態方法存到了哪個地方呢?為此我們還需要對這塊內存區域進行一個分析:
我給出了一張java7的運行時數據區劃分圖,對于每一個區域的基本情況我相信你也能看明白。那么我們的多態方法到底存在了哪呢?沒錯就是后一個方法區。java堆存的是就是我們建立的一個個實例對象,而方法區存的就是類的類型信息。
而且這個方法區中的類型信息跟在堆中存放的class對象是不同的。在方法區中,這個class的類型信息只有唯一的實例(所以方法區是各個線程共享的內存區域),而在堆中可以有多個該class對象。也就是說方法區的類型信息就是像一個模板,那些class對象就好比通過這些模板創建的一個個實例。
2、通過例子來分析
現在我們拿上面的例子來說明一下多態在java虛擬機中是如何實現的。在測試類中有兩行代碼:
FathersonA=newSonA();
FathersonB=newSonB();
當程序運行到FathersonA=newSonA()這里就出現了多態,這是因為編譯時看到Father,但是運行時new出來一個SonA類,兩種類型還不一樣。那么這些代碼在運行的時候在內存中是如何保存的呢?
(1)FathersonA是一個引用類型,存在了java棧中的本地方法表中了。
?。?)newSonA其實創建了一個實例對象,存儲在了java堆中。
?。?)SonA的類型數據存在了方法區中
我們在內存中看一下:
reference中存儲的就是對象在堆中的實際地址,在堆中存儲的對象信息中包含了在方法區中的相應類型數據。流程很簡單,我們梳理一下:
第一步:虛擬機通過reference(Father的引用)查詢java棧中的本地變量表,得到堆中的對象類型數據的指針,
第二步:通過到對象的指針找到方法區中的對象類型數據
第三步:查詢方法表定位到實際類(SonA類)的方法運行。
好了,到第三步我們知道,其實是通過方法表來定位到實際運行的方法的。下面我們再來看看這個方法表是什么。
3、方法表
方法表肯定是存在于方法區中的,它是實現多態的關鍵所在,這里面保存的就是實例方法的引用,而且是直接引用。java虛擬機在執行程序的時候就是通過這個方法表來確定運行哪一個多態方法的。
我們通過上面的例子,來演示一下父子類在方法表中是如何保存的:
很明顯每一個類都會有一個方法表,子類中不同的方法指向不同的類型信息。繼承自Object的就指向Object,繼承自Father的就指向Father(也就是包含了父類的方法dealHouse)。
可能我們到這就迷糊了,既然子類的dealHouse方法其實是父類Father的,那么為什么會執行子類的dealHouse方法呢?別著急往下看。這是java虛擬機區分多態方法(實現動態調用)的精華所在。
當Son類的方法表會有一個指向Father類dealHouse方法的指針,同時也有一個指向自己dealHouse方法的指針,這時候,新的數據會覆蓋原有的數據,也就是說原來指向Father.dealHouse的那個引用會被替換成指向Son.dealHouse的引用(占據原來表中的位置)
注意:
上述講述的其實是對繼承實現的多態的一種分析,對接口實現的,會有著不一樣的理解。Java虛擬機對于接口方法的調用是采用搜索方法表的方式,如,要在Father接口的方法表中找到dealHouse()方法,必須搜索Father的整個方法表。從效率上來說,接口方法的調用總是慢于類方法的調用的。
以上就是對java多態的分析與理解,總結一下就是說,類調用和接口調用兩種方式區分不同方法是不一樣的,類調用是根據多態方法在方法表中的位移量,而接口調用是根據搜索整個方法表來實現的。
以上就是動力節點Java培訓機構小編介紹的“Javase入門教程:深入分析Java多態”的內容,希望對大家有幫助,如有疑問,請在線咨詢,有專業老師隨時為你服務。
0基礎 0學費 15天面授
有基礎 直達就業
業余時間 高薪轉行
工作1~3年,加薪神器
工作3~5年,晉升架構
提交申請后,顧問老師會電話與您溝通安排學習