Carson帶你學(xué)JVM:方法分派模型-靜態(tài)分派、動(dòng)態(tài)分派

前言

  • 了解 行為方法分派 有利于在行為分派時(shí)時(shí)進(jìn)行一些功能操作
  • 本文全面講解行為分派的類型:靜態(tài) & 動(dòng)態(tài)行為分派,希望你們會喜歡。

Carson帶你學(xué)JVM系列文章,具體如下:
Carson帶你學(xué)JVM:這是一份全面 & 詳細(xì)的JVM學(xué)習(xí)指南
Carson帶你學(xué)JVM:圖文解析Java虛擬機(jī)內(nèi)存結(jié)構(gòu)
Carson帶你學(xué)JVM:Java對象的創(chuàng)建、內(nèi)存布局 & 訪問定位全過程解析
Carson帶你學(xué)JVM:Java對象如何判斷存活原則-引用計(jì)數(shù)法 & 引用鏈法
Carson帶你學(xué)JVM:這是一份全面 & 詳細(xì)的垃圾收集算法(GC)講解攻略
Carson帶你學(xué)JVM:常見的垃圾收集器學(xué)習(xí)指南
Carson帶你學(xué)JVM:類加載的全過程解析
Carson帶你學(xué)JVM:你真的了解類加載器嗎?(含雙親委派模型)
Carson帶你學(xué)JVM:方法分派模型-靜態(tài)分派、動(dòng)態(tài)分派


目錄

示意圖

1. 知識儲備

1.1 分派

  • 定義:確定執(zhí)行哪個(gè)方法 的過程

a. 疑問
有些讀者會問,方法的執(zhí)行不是取決于代碼設(shè)置中的執(zhí)行對象嗎?為什么還要選擇呢?
b. 回答

  • 若 一個(gè)對象對應(yīng)于多個(gè)方法 時(shí),就需要進(jìn)行選擇了
  • 讀者應(yīng)該都想到了 Java中的特性:多態(tài),即重寫 & 重載。下面我會詳細(xì)講解。
  • 分類:靜態(tài)分派 & 動(dòng)態(tài)分派。下面我將詳細(xì)講解。

1.2 變量的靜態(tài)類型 & 動(dòng)態(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的動(dòng)態(tài)類型 = 實(shí)例對象類型 = Man:會變化、在運(yùn)行期才可知

    } 
}

即:

  • 變量的靜態(tài)類型 = 引用類型 :不會被改變、在編譯器可知
  • 變量的動(dòng)態(tài)類型 = 實(shí)例對象類型 :會變化、在運(yùn)行期才可知

下面,我將詳細(xì)講解Java中的分派類型:靜態(tài)分派 & 動(dòng)態(tài)分派


2. 靜態(tài)分派

  • 定義
    根據(jù) 變量的靜態(tài)類型 進(jìn)行方法分派 的 行為
  1. 即根據(jù) 變量的靜態(tài)類型 確定執(zhí)行哪個(gè)方法
  2. 發(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í)行(重載)哪個(gè)方法
  • 所以上述的方法執(zhí)行時(shí),是根據(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)換
// 此時(shí)man的靜態(tài)類型從 Human 變?yōu)?Man

// 所以會調(diào)用sayHello()中參數(shù)為Man guy的方法,即sayHello(Man guy)

b. 靜態(tài)分派的優(yōu)先級匹配問題

  • 問題描述:
  1. 背景
    現(xiàn)需要進(jìn)行靜態(tài)分派
  2. 問題
    程序中 沒有顯示指定 靜態(tài)類型
  3. 解決方案
    程序會根據(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(chǎn)’是一個(gè)char類型數(shù)據(jù)(即靜態(tài)類型是char),所以會選擇參數(shù)類型為char的重載方法。
  • 若注釋掉sayHello(char arg)方法,那么會輸出
hello int
  • 因?yàn)?code>‘a(chǎn)’除了可代表字符串,還可代表數(shù)字97。因此當(dāng)沒有最合適的sayHello(char arg)方式進(jìn)行重載時(shí),會選擇第二合適(第二優(yōu)先級)的方法重載,即
    sayHello(int arg)
  • 總結(jié):當(dāng)沒有最合適的方法進(jìn)行重載時(shí),會選優(yōu)先級第二高的的方法進(jìn)行重載,如此類推。
  1. 優(yōu)先級順序?yàn)椋?code>char>int>long>float>double>Character>Serializable>Object>...
  2. 其中...為變長參數(shù),將其視為一個(gè)數(shù)組元素。變長參數(shù)的重載優(yōu)先級最低。
  3. 因?yàn)?char 轉(zhuǎn)型到 byteshort 的過程是不安全的,所以不會選擇參數(shù)類型為byteshort的方法進(jìn)行重載,故優(yōu)先級列表里也沒有。

特別注意

  • 上面講解的主要是 基本數(shù)據(jù)類型的優(yōu)先級匹配問題
  • 若是引用類型,則根據(jù) 繼承關(guān)系 進(jìn)行優(yōu)先級匹配

注意只跟其編譯時(shí)類型(即靜態(tài)類型)相關(guān)


3. 動(dòng)態(tài)分派

  • 定義
    根據(jù) 變量的動(dòng)態(tài)類型 進(jìn)行方法分派 的 行為

即根據(jù) 變量的動(dòng)態(tài)類型 確定執(zhí)行哪個(gè)方法

  • 應(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) = 動(dòng)態(tài)分派 = 根據(jù) 變量的動(dòng)態(tài)類型 確定執(zhí)行(重寫)哪個(gè)方法
// 2. 對于情況1:根據(jù)變量(Man)的動(dòng)態(tài)類型(man)確定調(diào)用man中的重寫方法sayHello()
// 3. 對于情況2:根據(jù)變量(Man)的動(dòng)態(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ū)別

示意圖

5. 總結(jié)

  • 本文全面講解方法分派的類型 & 過程

  • 接下來我會對Java虛擬機(jī)(JVM)進(jìn)行詳細(xì)的分析,歡迎關(guān)注Carson_Ho的簡書,不定期分享關(guān)于安卓開發(fā)的干貨,追求短、平、快,但卻不缺深度


請點(diǎn)贊!因?yàn)槟愕墓膭?lì)是我寫作的最大動(dòng)力!

Carson帶你學(xué)JVM系列文章,具體如下:
Carson帶你學(xué)JVM:這是一份全面 & 詳細(xì)的JVM學(xué)習(xí)指南
Carson帶你學(xué)JVM:圖文解析Java虛擬機(jī)內(nèi)存結(jié)構(gòu)
Carson帶你學(xué)JVM:Java對象的創(chuàng)建、內(nèi)存布局 & 訪問定位全過程解析
Carson帶你學(xué)JVM:Java對象如何判斷存活原則-引用計(jì)數(shù)法 & 引用鏈法
Carson帶你學(xué)JVM:這是一份全面 & 詳細(xì)的垃圾收集算法(GC)講解攻略
Carson帶你學(xué)JVM:常見的垃圾收集器學(xué)習(xí)指南
Carson帶你學(xué)JVM:類加載的全過程解析
Carson帶你學(xué)JVM:你真的了解類加載器嗎?(含雙親委派模型)
Carson帶你學(xué)JVM:方法分派模型-靜態(tài)分派、動(dòng)態(tài)分派

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 今天跟Tomson教授聊到馬拉松這件事,他認(rèn)為跑步是最無聊的運(yùn)動(dòng)之一,但是他依然很開心,之前去法國參加了紅酒馬拉松...
    五月成長筆記閱讀 317評論 0 0
  • 人生或許各自相安無事地過下去是最好的吧,就連父母兄弟也是一樣的,他們都不是你,不會說:“你走吧,這一路風(fēng)景怎...
    卅良女子閱讀 198評論 0 0

友情鏈接更多精彩內(nèi)容