最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java方法調(diào)用解析靜態(tài)分派動(dòng)態(tài)分派執(zhí)行過程

 更新時(shí)間:2022年06月23日 16:52:24   作者:攻城獅Chova  
這篇文章主要為大家介紹了Java方法調(diào)用解析靜態(tài)分派動(dòng)態(tài)分派執(zhí)行過程,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

方法調(diào)用

在程序運(yùn)行時(shí),進(jìn)行方法調(diào)用是最普遍,最頻繁的操作

方法調(diào)用不等于方法執(zhí)行:

  • 方法調(diào)用階段唯一的任務(wù)就是確定被調(diào)用的方法版本,即調(diào)用哪一個(gè)方法
  • 不涉及方法內(nèi)部的具體運(yùn)行過程

Class文件的編譯過程不包括傳統(tǒng)編譯中的連接步驟

Class文件中的一切方法調(diào)用在Class文件里面存儲(chǔ)的都是符號(hào)引用,而不是方法在在實(shí)際運(yùn)行時(shí)內(nèi)存布局中的入口地址,即之前的直接引用:

  • 這樣使得Java具有更強(qiáng)大的動(dòng)態(tài)擴(kuò)展能力
  • 同時(shí)也使得Java方法調(diào)用過程變得相對(duì)復(fù)雜
  • 需要在類加載期間,甚至?xí)竭\(yùn)行期間才能確定目標(biāo)方法的直接引用

方法解析

所有方法調(diào)用中的目標(biāo)方法在Class文件里都是一個(gè)常量池的引用

在類的加載解析階段,會(huì)將其中的一部分符號(hào)引用轉(zhuǎn)化為直接引用:

方法在程序真正執(zhí)行之前就有一個(gè)可確定的調(diào)用版本,并且這個(gè)方法的調(diào)用版本在運(yùn)行期是不可改變的

也就是說,調(diào)用目標(biāo)在程序代碼中完成,編譯器進(jìn)行編譯時(shí)就必須確定下來,這也叫做方法解析

Java方法分類

在Java中符合 "編譯期可知,運(yùn)行期不可變" 的方法有兩大類:

  • 靜態(tài)方法: 與類型直接關(guān)聯(lián)
  • 私有方法: 在外部不可被訪問

這兩種方法各自的特點(diǎn)決定這兩種方法都不可能通過繼承或者別的方式重寫版本,因此適合在類加載階段進(jìn)行解析

非虛方法: 在類加載階段會(huì)把符號(hào)引用解析為該方法的直接引用

  • 靜態(tài)方法
  • 私有方法
  • 實(shí)例構(gòu)造器
  • 父類方法

虛方法: 在類加載階段不會(huì)將符號(hào)引用解析為該方法的直接引用

除去以上的非虛方法,其它的方法均為虛方法

靜態(tài)分派

public class StaticDispatch {
	static abstract class Human {
	}
	static class Man extends Human {
	}
	static class Woman extends Human {
	}
	public static void sayHello(Human guy) {
		System.out.println("Hello, Guy!");
	}
	public static void sayHello(Man guy) {
		System.out.println("Hello, Gentleman!");
	}
	public static void sayHello(woman guy) {
		System.out.println("Hello, Lady!");
	}
	public static void main(String[] args) {
		Human man = new Man();
		Human women = new Woman();
		sayHello(man);
		sayHello(woman);
	}
}

Human man = new Human();

Human為變量的靜態(tài)類型

Man為變量的實(shí)際類型

靜態(tài)類型和實(shí)際類型在程序中都會(huì)放生變化:

靜態(tài)類型:

  • 靜態(tài)類型的變化僅僅在使用時(shí)發(fā)生
  • 變量本身的靜態(tài)類型不會(huì)被改變
  • 最終的靜態(tài)類型在編譯器中可知

實(shí)際類型:

  • 實(shí)際類型變化的結(jié)果在運(yùn)行期才確定下來
  • 編譯器在編譯期間并不知道一個(gè)對(duì)象的實(shí)際類型是什么
Human human = new Man();
sayHello(man);
sayHello((Man)man);		// 類型轉(zhuǎn)換,靜態(tài)類型變化,轉(zhuǎn)型后的靜態(tài)類型一定是Man
man = new woman();		// 實(shí)際類型變化,實(shí)際類型是不確定的
sayHello(man);
sayHello((Woman)man);	// 類型轉(zhuǎn)換,靜態(tài)類型變化

編譯器在重載時(shí)是通過參數(shù)的靜態(tài)類型而不是實(shí)際類型作為判斷依據(jù),靜態(tài)類型在編譯期間可以知道:

編譯階段,Javac編譯器會(huì)根據(jù)參數(shù)的靜態(tài)類型決定使用哪個(gè)重載版本

靜態(tài)分派:

  • 所有依賴靜態(tài)類型來定位方法的執(zhí)行版本的分派動(dòng)作
  • 典型應(yīng)用 :方法重載

靜態(tài)分派發(fā)生在編譯階段,因此確定靜態(tài)分派的的動(dòng)作不是由虛擬機(jī)執(zhí)行的,而是由編譯器完成的

由于字面量沒有顯示靜態(tài)類型,只能通過語(yǔ)言上的規(guī)則去理解和推斷

public class LiteralTest {
	public static void sayHello(char arg) {
		System.out.println("Hello, char!");
	}
	public static void sayHello(int arg) {
		System.out.println("Hello, int!");
	}
	public static void sayHello(long arg) {
		System.out.println("Hello, long!");
	}
	public static void sayHello(Character arg) {
		System.out.println("Hello, Character!");
	}
	public static void main(String[] arg) {
		sayHello('a');
	}
}

編譯器將重載方法從上向下依次注釋,得到不同的輸出

如果編譯器無(wú)法確定要自定轉(zhuǎn)型為哪種類型,會(huì)提示類型模糊,拒絕編譯

public class LiteralTest {
	public static void sayHello(String arg) {	// 新增重載方法
		System.out.println("Hello, String!");
	}
	public static void sayHello(char arg) {	
		System.out.println("Hello, char!");
	}
	public static void sayHello(int arg) {
		System.out.println("Hello, int!");
	}
	public static void sayHello(long arg) {
		System.out.println("Hello, long!");
	}
	public static void sayHello(Character arg) {
		System.out.println("Hello, Character!");
	}
	public static void main(String[] args) {
		Random r = new Random();
		String s = "abc";
		int i = 0;
		sayHello(r.nextInt() % 2 != 0 ? s : 1 );	// 編譯錯(cuò)誤
		sayHello(r.nextInt() % 2 != 0 ? 'a' : false);	//編譯錯(cuò)誤
	}
}

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

public class DynamicDispatch {
	static abstract class Human {
		protected abstract void sayHello();
	}
	static class Man extends Human {
		@override
		protected void sayHello() {
			System.out.println("Man Say Hello!");
		}
	}
	static class Woman extends Human {
		@override
		protected void sayHello() {
			System.out.println("Woman Say Hello!");
		}
	}
	public static void main(String[] args) {
		Human man = new Man();
		Human women = new Woman();
		man.sayHello();
		woman.sayHello();
		man = new Woman();
		man.sayHello();
	}
}

這里不是根據(jù)靜態(tài)類型決定的

  • 靜態(tài)類型的Human兩個(gè)變量man和woman在調(diào)用sayHello() 方法時(shí)執(zhí)行了不同的行為
  • 變量man在兩次調(diào)用中執(zhí)行了不同的方法

導(dǎo)致這個(gè)現(xiàn)象的額原因 :這兩個(gè)變量的實(shí)際類型不同

Java虛擬機(jī)是如何根據(jù)實(shí)際類型分派方法的執(zhí)行版本的: 從invokevirtual指令的多態(tài)查找過程開始 ,invokevirtual指令運(yùn)行時(shí)解析過程大致分為以下幾個(gè)步驟:

  • 找到操作數(shù)棧頂?shù)牡谝粋€(gè)元素所指向的對(duì)象的實(shí)際類型,記作C
  • 如果在類型C中找到與常量中的描述符和簡(jiǎn)單名稱相符合的方法,然后進(jìn)行訪問權(quán)限驗(yàn)證,如果驗(yàn)證通過則返回這個(gè)方法的直接引用,查找過程結(jié)束;如果驗(yàn)證不通過,則拋出java.lang.illegalAccessError異常
  • 如果未找到,就按照繼承關(guān)系從下往上依次對(duì)類型C的各個(gè)父類進(jìn)行第二步的搜索和驗(yàn)證過程
  • 如果始終沒有找到合適的方法,則拋出java.lang.AbstractMethodError異常

Java語(yǔ)言方法重寫的本質(zhì):

invokevirtual指令執(zhí)行的第一步就是在運(yùn)行時(shí)期確定接收者的實(shí)際類型,所以兩次調(diào)用中的invokevirtual指令把常量池中的類方法符號(hào)引用解析到了不同的直接引用上

這種在運(yùn)行時(shí)期根據(jù)實(shí)際類型確定方法執(zhí)行版本的分派過程就叫做動(dòng)態(tài)分派

虛擬機(jī)動(dòng)態(tài)分派的實(shí)現(xiàn)

虛擬機(jī)概念解析的模式就是靜態(tài)分派和動(dòng)態(tài)分派,可以理解虛擬機(jī)在分派中 "會(huì)做什么" 這個(gè)問題

虛擬機(jī) "具體是如何做到的" 在各種虛擬機(jī)實(shí)現(xiàn)上會(huì)有差別:

  • 由于動(dòng)態(tài)分派是非常頻繁的動(dòng)作,而且動(dòng)態(tài)分派的方法版本選擇過程需要運(yùn)行時(shí)在類的方法元數(shù)據(jù)中搜索合適的目標(biāo)方法
  • 因此在虛擬機(jī)的實(shí)際實(shí)現(xiàn)中,為了基于性能的考慮,大部分實(shí)現(xiàn)都不會(huì)真正的進(jìn)行如此頻繁的搜索
  • 最常用的"穩(wěn)定優(yōu)化"的方式是為類在方法區(qū)中建立一個(gè)虛方法表(Virtual Method Table,即vtable), 使用虛方法表索引代替元數(shù)據(jù)查找以提高性能

虛方法表中存放著各個(gè)方法的實(shí)際入口地址:

  • 如果某個(gè)方法在子類中沒有被重寫,那子類的虛方法表里面的地址入口和父類相同方法的地址入口是一致的,都指向父類的實(shí)際入口
  • 如果子類中重寫了這個(gè)方法,子類方法表中的地址將會(huì)替換為指向子類實(shí)際方法的入口地址

具有相同簽名的方法,在父類,子類的虛方法表中具有一樣的索引序號(hào):

這樣當(dāng)類型變換時(shí),僅僅需要變更查找的方法表,就可以從不同的虛方法表中按索引轉(zhuǎn)換出所需要的入口地址

方法表一般在類加載階段的連接階段進(jìn)行初始化:

準(zhǔn)備了類的變量初始值后,虛擬機(jī)會(huì)把該類的方法表也初始化完畢

以上就是Java方法調(diào)用解析靜態(tài)分派動(dòng)態(tài)分派執(zhí)行過程的詳細(xì)內(nèi)容,更多關(guān)于Java靜態(tài)動(dòng)態(tài)分派執(zhí)行過程的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • SpringCloud Stream消息驅(qū)動(dòng)實(shí)例詳解

    SpringCloud Stream消息驅(qū)動(dòng)實(shí)例詳解

    這篇文章主要介紹了SpringCloud Stream消息驅(qū)動(dòng)的相關(guān)知識(shí),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-03-03
  • Spring BPP中如何優(yōu)雅的創(chuàng)建動(dòng)態(tài)代理Bean詳解

    Spring BPP中如何優(yōu)雅的創(chuàng)建動(dòng)態(tài)代理Bean詳解

    這篇文章主要給大家介紹了關(guān)于Spring BPP中如何優(yōu)雅的創(chuàng)建動(dòng)態(tài)代理Bean的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • JAVA十大排序算法之冒泡排序詳解

    JAVA十大排序算法之冒泡排序詳解

    這篇文章主要介紹了java中的冒泡排序,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-08-08
  • Java中的Cookie和Session詳細(xì)解析

    Java中的Cookie和Session詳細(xì)解析

    這篇文章主要介紹了Java中的Cookie和Session詳細(xì)解析,客戶端會(huì)話技術(shù),服務(wù)端給客戶端的數(shù)據(jù),存儲(chǔ)于客戶端(瀏覽器),由于是保存在客戶端上的,所以存在安全問題,需要的朋友可以參考下
    2024-01-01
  • java中Supplier知識(shí)點(diǎn)總結(jié)

    java中Supplier知識(shí)點(diǎn)總結(jié)

    在本篇文章里小編給大家整理的是一篇關(guān)于java中Supplier知識(shí)點(diǎn)總結(jié)內(nèi)容,有興趣的朋友們可以學(xué)習(xí)下。
    2021-04-04
  • springboot整合Dubbo與Feign的實(shí)現(xiàn)?(無(wú)注冊(cè)中心)

    springboot整合Dubbo與Feign的實(shí)現(xiàn)?(無(wú)注冊(cè)中心)

    本文主要介紹了springboot整合Dubbo與Feign的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-04-04
  • Java中JSONObject和Map<String,?Object>的轉(zhuǎn)換方法

    Java中JSONObject和Map<String,?Object>的轉(zhuǎn)換方法

    平時(shí)對(duì)接口時(shí),經(jīng)常遇到j(luò)son字符串和map對(duì)象之間的交互,這篇文章主要給大家介紹了關(guān)于Java中JSONObject和Map<String,?Object>的轉(zhuǎn)換方法,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2024-07-07
  • Spring實(shí)戰(zhàn)之方法級(jí)別緩存用法示例

    Spring實(shí)戰(zhàn)之方法級(jí)別緩存用法示例

    這篇文章主要介紹了Spring實(shí)戰(zhàn)之方法級(jí)別緩存用法,結(jié)合實(shí)例形式分析了spring方法級(jí)別緩存配置、屬性文件、領(lǐng)域模型及相關(guān)使用技巧,需要的朋友可以參考下
    2020-01-01
  • IntelliJ IDEA運(yùn)行bat腳本,自動(dòng)taskkill端口進(jìn)程

    IntelliJ IDEA運(yùn)行bat腳本,自動(dòng)taskkill端口進(jìn)程

    這篇文章主要介紹了IDEA里面無(wú)法運(yùn)行bat文件的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-11-11
  • SpringBoot實(shí)現(xiàn)API接口的完整代碼

    SpringBoot實(shí)現(xiàn)API接口的完整代碼

    這篇文章主要給大家介紹了關(guān)于SpringBoot實(shí)現(xiàn)API接口的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-10-10

最新評(píng)論

都安| 南川市| 无为县| 额尔古纳市| 萨嘎县| 额敏县| 阿拉善盟| 博野县| 延安市| 云安县| 宣恩县| 元阳县| 大宁县| 长沙市| 茶陵县| 农安县| 鄂托克前旗| 海口市| 灵璧县| 招远市| 枝江市| 黎平县| 山丹县| 彰化市| 朝阳市| 永川市| 广元市| 嘉义市| 富锦市| 秭归县| 扎兰屯市| 壶关县| 横山县| 宜川县| 沅江市| 佛冈县| 广安市| 和平区| 措美县| 灯塔市| 利辛县|