java 多态实现的jvm调用过程_多态:JVM是如何进行方法调用的
在我們平時的工作學習中寫java代碼時,如果我們在同一個類中定義了兩個方法名和參數(shù)類型都相同的方法時,編譯器會直接報錯給我們。還有在代碼運行的時候,如果子類定義了一個與父類完全相同的方法的時候,父類的方法就會被覆蓋,(也就是我們平時說的重寫)。那么,jvm虛擬機是如何精確識別目標方法的。
重載、重寫與多態(tài)
重載:方法名相同而參數(shù)類型不相同的方法之間的關系。
重寫:方法名相同并且參數(shù)類型也相同的方法之間的關系。
這兩個概念我們耳熟能詳,那么重載和重寫是如何判斷的呢?
重載:
重載的方法在編譯期間就可以完成識別。java編譯器會根據(jù)所傳入?yún)?shù)的聲明類型對方法名相同的方法進行選取。
除了同一個類中,如果A繼承了B,A中定義了與B中的非私有方法同名的方法,而且這兩個方法的參數(shù)類型不同,那么A和B類同樣構成了重載
重寫:
重寫方法的判斷是在運行期間才可以完成識別的。
我們都知道多態(tài)是java面向?qū)ο笳Z言的三大特性之一。而方法的重寫,就是最能體現(xiàn)多態(tài)的一種方式:它允許子類在繼承父類部分特性的同時,擁有自己獨特的行為。
舉個簡單的例子幫大家理解一下多態(tài):
比如我們按下b這個鍵,在dota中代表的是敵法師的blink技能,在lol中是回城,在網(wǎng)游里又成了背包。對于不同的對象擁有不同的行為,這就是多態(tài)。
靜態(tài)綁定與動態(tài)綁定
接下來,我們來看一下jvm是如何識別目標方法的。
剛才我們說到,重載方法的區(qū)分在編譯階段已經(jīng)完成了,那么我們就可以認為在java虛擬機中不存在重載這一概念。因此,重載也可以被稱為靜態(tài)綁定,而重寫則被稱為動態(tài)綁定。
在jvm中,我們有5種方法調(diào)用的指令,分別是:
invokestatic:調(diào)用靜態(tài)方法;
invokespecial:調(diào)用實例構造方法,私有方法和父類方法,以及使用super關鍵字調(diào)用父類的實例方法或構造器;
invokevirtual:調(diào)用虛方法(非私有實例方法);
invokeinterface:調(diào)用接口方法,在運行時再確定一個實現(xiàn)此接口的對象;
invokedynamic:在運行時動態(tài)解析出調(diào)用點限定符所引用的方法之后,調(diào)用該方法(jdk1.8lamada表達式);
這里,我們簡單介紹一下這幾種指令,對于invokestatic指令和invokespecial指令而言,java虛擬機能夠直接識別目標方法,也就是我們所說的靜態(tài)綁定。
invokevirtual和invokeinterface指令則需要在執(zhí)行的過程中才能找到目標方法,也就是我們所說的動態(tài)綁定。
總結一下靜態(tài)綁定和動態(tài)綁定的概念就是:
靜態(tài)綁定:在程序執(zhí)行之前就已經(jīng)被綁定、也就是說再編譯階段就已經(jīng)知道這個方法是屬于哪個類的方法。
1.private修飾的方法,不能被子類調(diào)用? ?2.? 被final修飾的方法? ? ? 3.被static修飾的方法
動態(tài)綁定:在運行過程中根據(jù)調(diào)用者的動態(tài)類型來識別目標方法的情況。
動態(tài)綁定中,我們會記錄方法對應的實際引用的地址,也可以理解為索引值,這里我們把它叫做方法表。
方法表使用了數(shù)組的數(shù)據(jù)結構,每個數(shù)組元素指向了當前類以及其祖先類中非私有的實例方法。
這個數(shù)據(jù)結構,便是java虛擬機實現(xiàn)動態(tài)綁定的關鍵所在
虛方法
通過這些指令的描述,我們發(fā)現(xiàn)虛方法和非虛方法直接決定了靜態(tài)綁定還是動態(tài)綁定,也就決定了是直接用父類的方法還是動態(tài)地用子類重寫的方法。所以,我們有必要去理解虛方法,并能判斷哪些是屬于虛方法。
我們先一起看兩段代碼:
代碼1:class Dota {private void play() {System.out.println("我喜歡玩dota,哈哈哈~~~");}void startGame() {play();}}?class LoLextends Dota {void play() {System.out.println("我喜歡玩lol,哈哈哈~~~~");}}?public class Demo1{public static void main(String[]args){new LoL().startGame();}}
代碼二:class Dota {void play() {System.out.println("我喜歡玩dota,哈哈哈~~~");}?void startGame() {play();}}?class LoLextends Dota {void play() {System.out.println("我喜歡玩lol,哈哈哈~~~~");}}?public class Demo2{public static void main(String[]args){new LoL().startGame();}}
這里,dota是lol的父類(本人是dotaer,哈哈),這兩段代碼的唯一不同就是代碼1的父類的play方法private修飾的,而代碼2中的play()方法不是私有的。接下來,我們看下輸出結果:
代碼1:我喜歡玩dota,哈哈哈~~~
代碼2:1我喜歡玩lol,哈哈哈~~~~
第一段代碼直接調(diào)用了父類的play()方法,而第二段代碼調(diào)用了子類的play()方法
大家是不是覺得很奇怪,一個私有的修飾符就能使結果不一樣嗎?
結合我們之前所說的進行判別,第一段代碼應該是靜態(tài)綁定,第二段代碼則是動態(tài)綁定
那么,代碼1就是非虛函數(shù),代碼2是虛函數(shù)。接下來,我們來看下虛函數(shù)的概念:
虛函數(shù):除了靜態(tài)方法之外,聲明為final或者private的實例方法是非虛方法。其它(其他非private方法)實例方法都是虛方法。
當子類對象調(diào)用重寫的方法時,調(diào)用的是子類的方法,而不是父類中被重寫的方法。
要想調(diào)用父類中被重寫的方法,則必須使用關鍵字super。
代碼1由于是private修飾,所以為非虛函數(shù),調(diào)用了invokespecial指令。
代碼2是虛函數(shù),則調(diào)用了invokevirtual指令。
再看一個例子
代碼3:
public?class?Demo2?{????static?abstract?class?Game?{}????static?class?Dota?extends?Game?{}????static?class?Lol?extends?Game?{}????public?void?play(Game?game)?{
System.out.println("hello,game");
}????public?void?play(Dota?dota)?{
System.out.println("hello,Dota");
}????public?void?play(Lol?lol)?{
System.out.println("hello,lol");
}????public?static?void?main(String[]?args)?{
Game?dota?=?new?Dota();
Game?lol?=?new?Lol();
Demo2?sd?=?new?Demo2();
sd.play(dota);
sd.play(lol);
}
}
輸出結果:hello,game
hello,game
雖然在這里,play方法是虛方法,是動態(tài)綁定,但是調(diào)用play方法的是Demo2的實例sd,由于Demo2方法沒有子類,所以不需要考慮,則直接執(zhí)行父類的方法。
總結:
我們介紹了java虛擬機(jvm)是如何執(zhí)行方法的,我們從我們熟悉的重載和重寫切入,了解了靜態(tài)綁定和動態(tài)綁定的概念:
靜態(tài)綁定:在程序執(zhí)行之前就已經(jīng)被綁定、也就是說再編譯階段就已經(jīng)知道這個方法是屬于哪個類的方法。
動態(tài)綁定:在運行過程中根據(jù)調(diào)用者的動態(tài)類型來識別目標方法的情況
并且知道了jvm中用方法表來維護非私有實例方法與其索引值的對應關系來實現(xiàn)動態(tài)綁定。
接著,我們了解了jvm中調(diào)用方法的5個指令,并且通過靜態(tài)綁定和動態(tài)綁定的概念對其進行歸類,
靜態(tài)綁定:
invokestatic:調(diào)用靜態(tài)方法;
invokespecial:調(diào)用實例構造方法,私有方法和父類方法;
動態(tài)綁定:
invokevirtual:調(diào)用虛方法;
invokeinterface:調(diào)用接口方法,在運行時再確定一個實現(xiàn)此接口的對象;
invokedynamic:在運行時動態(tài)解析出調(diào)用點限定符所引用的方法之后,調(diào)用該方法;
歸類中通過虛函數(shù)的概念,使我們對jvm中方法的調(diào)用加以更深的了解。
虛函數(shù):除了靜態(tài)方法之外,聲明為final或者private的實例方法是非虛方法。其它(其他非private方法)實例方法都是虛方法。
總結
以上是生活随笔為你收集整理的java 多态实现的jvm调用过程_多态:JVM是如何进行方法调用的的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mysql+性能优化+命令_MySQL性
- 下一篇: datagridview 手动添加的行如