java?JVM方法分派模型靜態(tài)分派動態(tài)分派全面講解
前言
- 了解 行為方法分派 有利于在行為分派時時進(jìn)行一些功能操作
- 本文全面講解行為分派的類型:靜態(tài) & 動態(tài)行為分派,希望你們會喜歡。
目錄結(jié)構(gòu)圖

1. 知識儲備
1.1 分派
- 定義:確定執(zhí)行哪個方法 的過程
a. 疑問
有些讀者會問,方法的執(zhí)行不是取決于代碼設(shè)置中的執(zhí)行對象嗎?為什么還要選擇呢?
b. 回答
- 若 一個對象對應(yīng)于多個方法 時,就需要進(jìn)行選擇了
- 讀者應(yīng)該都想到了
Java中的特性:多態(tài),即重寫 & 重載。下面我會詳細(xì)講解。 - 分類:靜態(tài)分派 & 動態(tài)分派。下面我將詳細(xì)講解。
1.2 變量的靜態(tài)類型 & 動態(tài)類型
先看下面的代碼
public class Test {
static abstract class Human {
}
static class Man extends Human {
}
static class Woman extends Human {
}
// 執(zhí)行代碼
public static void main(String[] args) {
Human man = new Man();
// 變量man的靜態(tài)類型 = 引用類型 = Human:不會被改變、在編譯器可知
// 變量man的動態(tài)類型 = 實(shí)例對象類型 = Man:會變化、在運(yùn)行期才可知
}
}即:
- 變量的靜態(tài)類型 = 引用類型 :不會被改變、在編譯器可知
- 變量的動態(tài)類型 = 實(shí)例對象類型 :會變化、在運(yùn)行期才可知
下面,我將詳細(xì)講解Java中的分派類型:靜態(tài)分派 & 動態(tài)分派
2. 靜態(tài)分派
定義: 根據(jù) 變量的靜態(tài)類型 進(jìn)行方法分派 的 行為
- 即根據(jù) 變量的靜態(tài)類型 確定執(zhí)行哪個方法
- 發(fā)生在編譯期,所以不由
Java虛擬機(jī)來執(zhí)行
應(yīng)用場景: 方法重載(OverLoad)
實(shí)例說明
public class Test {
// 類定義
static abstract class Human {
}
// 繼承自抽象類Human
static class Man extends Human {
}
static class Woman extends Human {
}
// 可供重載的方法
public void sayHello(Human guy) {
System.out.println("hello,guy!");
}
public void sayHello(Man guy) {
System.out.println("hello gentleman!");
}
public void sayHello(Woman guy) {
System.out.println("hello lady!");
}
// 測試代碼
public static void main(String[] args) {
Human man = new Man();
Human woman = new Woman();
Test test = new Test();
test.sayHello(man);
test.sayHello(woman);
}
}
// 運(yùn)行結(jié)果
hello,guy!
hello,guy!根據(jù)上述的講解,大家應(yīng)該明白運(yùn)行結(jié)果的原因:
- 方法重載(
OverLoad) = 靜態(tài)分派 = 根據(jù) 變量的靜態(tài)類型 確定執(zhí)行(重載)哪個方法 - 所以上述的方法執(zhí)行時,是根據(jù)變量(
man、woman)的靜態(tài)類型(Human)確定重載sayHello()中參數(shù)為Human guy的方法,即sayHello(Human guy)
特別注意
a. 變量的靜態(tài)類型 發(fā)生變化 的情況
可通過 強(qiáng)制類型轉(zhuǎn)換 改變 變量的靜態(tài)類型
Human man = new Man(); test.sayHello((Man)man); // 強(qiáng)制類型轉(zhuǎn)換 // 此時man的靜態(tài)類型從 Human 變?yōu)?Man // 所以會調(diào)用sayHello()中參數(shù)為Man guy的方法,即sayHello(Man guy)
b. 靜態(tài)分派的優(yōu)先級匹配問題
問題描述:
- 背景 現(xiàn)需要進(jìn)行靜態(tài)分派
- 問題 程序中 沒有顯示指定 靜態(tài)類型
- 解決方案 程序會根據(jù) 靜態(tài)類型的優(yōu)先級 從而選擇 優(yōu)先的靜態(tài)類型進(jìn)行方法分配。
實(shí)例說明
public class Overload {
private static void sayHello(char arg){
System.out.println("hello char");
}
private static void sayHello(Object arg){
System.out.println("hello Object");
}
private static void sayHello(int arg){
System.out.println("hello int");
}
private static void sayHello(long arg){
System.out.println("hello long");
}
// 測試代碼
public static void main(String[] args) {
sayHello('a');
}
}
// 運(yùn)行結(jié)果
hello char因?yàn)?code>‘a’是一個char類型數(shù)據(jù)(即靜態(tài)類型是char),所以會選擇參數(shù)類型為char的重載方法。
若注釋掉sayHello(char arg)方法,那么會輸出
hello int
因?yàn)?code>‘a’除了可代表字符串,還可代表數(shù)字97。因此當(dāng)沒有最合適的sayHello(char arg)方式進(jìn)行重載時,會選擇第二合適(第二優(yōu)先級)的方法重載,即 sayHello(int arg)
總結(jié):當(dāng)沒有最合適的方法進(jìn)行重載時,會選優(yōu)先級第二高的的方法進(jìn)行重載,如此類推。
優(yōu)先級順序?yàn)椋?/p>
char>int>long>float>double>Character>Serializable>Object>...
其中...為變長參數(shù),將其視為一個數(shù)組元素。變長參數(shù)的重載優(yōu)先級最低。
因?yàn)?char 轉(zhuǎn)型到 byte 或 short 的過程是不安全的,所以不會選擇參數(shù)類型為byte 或 short的方法進(jìn)行重載,故優(yōu)先級列表里也沒有。
特別注意
- 上面講解的主要是 基本數(shù)據(jù)類型的優(yōu)先級匹配問題
- 若是引用類型,則根據(jù) 繼承關(guān)系 進(jìn)行優(yōu)先級匹配
注意只跟其編譯時類型(即靜態(tài)類型)相關(guān)
3. 動態(tài)分派
- 定義 根據(jù) 變量的動態(tài)類型 進(jìn)行方法分派 的 行為
即根據(jù) 變量的動態(tài)類型 確定執(zhí)行哪個方法
- 應(yīng)用場景 方法重寫(
Override) - 實(shí)例說明
// 定義類
class Human {
public void sayHello(){
System.out.println("Human say hello");
}
}
// 繼承自 抽象類Human 并 重寫sayHello()
class Man extends Human {
@Override
protected void sayHello() {
System.out.println("man say hello");
}
}
class Woman extends Human {
@Override
protected void sayHello() {
System.out.println("woman say hello");
}
}
// 測試代碼
public static void main(String[] args) {
// 情況1
Human man = new man();
man.sayHello();
// 情況2
man = new Woman();
man.sayHello();
}
}
// 運(yùn)行結(jié)果
man say hello
woman say hello
// 原因解析
// 1. 方法重寫(Override) = 動態(tài)分派 = 根據(jù) 變量的動態(tài)類型 確定執(zhí)行(重寫)哪個方法
// 2. 對于情況1:根據(jù)變量(Man)的動態(tài)類型(man)確定調(diào)用man中的重寫方法sayHello()
// 3. 對于情況2:根據(jù)變量(Man)的動態(tài)類型(woman)確定調(diào)用woman中的重寫方法sayHello()特別注意
對于代碼中:
Human man = new Man(); man = new Woman(); man.sayHello(); // man稱為執(zhí)行sayHello()方法的所有者,即接受者。
invokevirtual指令執(zhí)行的第一步 = 確定接受者的實(shí)際類型invokevirtual指令執(zhí)行的第二步 = 將 常量池中 類方法符號引用 解析到不同的直接引用上
第二步即方法重寫(Override)的本質(zhì)
4. 二者區(qū)別

總結(jié)
本文全面講解方法分派的類型 & 過程,更多關(guān)于java JVM靜態(tài)動態(tài)分派模型的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
springboot配置項(xiàng)目啟動后自動打開瀏覽器訪問項(xiàng)目方式
這篇文章主要介紹了springboot配置項(xiàng)目啟動后自動打開瀏覽器訪問項(xiàng)目方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-01-01
更簡單更高效的Mybatis?Plus最新代碼生成器AutoGenerator
這篇文章主要為大家介紹了更簡單更高效的Mybatis?Plus最新代碼生成器AutoGenerator使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02
SpringCloud超詳細(xì)講解Feign聲明式服務(wù)調(diào)用
Feign可以把Rest的請求進(jìn)行隱藏,偽裝成類似Spring?MVC的Controller一樣。不用再自己拼接url,拼接參數(shù)等等操作,一切都交給Feign去做2022-06-06
MyBatis之自查詢使用遞歸實(shí)現(xiàn) N級聯(lián)動效果(兩種實(shí)現(xiàn)方式)
這篇文章主要介紹了MyBatis之自查詢使用遞歸實(shí)現(xiàn) N級聯(lián)動效果,本文給大家分享兩種實(shí)現(xiàn)方式,需要的的朋友參考下吧2017-07-07

