搜尋
首頁Javajava教程Java中靜態分派和動態分派的介紹(程式碼範例)

這篇文章帶給大家的內容是關於Java中靜態分派和動態分派的介紹(程式碼範例),有一定的參考價值,有需要的朋友可以參考一下,希望對你有幫助。

最近複習JVM的知識,對於靜態分派和動態分派的理解有點混亂,於是自己嘗試寫寫程式碼,在分析中鞏固知識。

有以下一段程式碼,請問每一段分別輸出什麼?

package com.khlin.my.test;

class Base {

    public static void foo() {
        System.out.println("Base.foo() invoked");
    }

    public void bar(int c) {
        System.out.println("Base.bar(int) invoked");
    }

    public void bar(Character c) {
        System.out.println("Base.bar(Character) invoked");
    }

    public void baz(Object o) {
        System.out.println("Base.baz(Object) invoked");
    }

    public void baz(Integer i) {
        System.out.println("Base.baz(Integer) invoked");
    }

}

class Child extends Base {
    public static void foo() {
        System.out.println("Child.foo() invoked");
    }

    public void bar(Character c) {
        System.out.println("Child.bar(Character) invoked");
    }

    public void bar(char c) {
        System.out.println("Child.bar(char) invoked");
    }
}

public class App {

    public static void main(String[] args) {
        Base child = new Child();

        System.out.println("第1段输出:");
        child.foo();
        child.bar(new Character('C'));

        System.out.println("第2段输出:");
        Object integer = new Integer(100);
        child.baz(integer);

        System.out.println("第3段输出:");
        child.bar('C');

    }
}

下面我簡單地介紹一下從程式碼編譯到方法呼叫的整個過程。

· 編譯

先看看第1段輸出,child.foo()是呼叫父類別還是子類別的靜態方法呢?

在編譯階段,發生了靜態分派

1 Base child = new Child();

在我們建立一個物件時,如上圖,Base稱為變數的靜態類型(Static Type), 或叫做外觀類型(Apparent Type) ,後面的Child則稱為變數的實際類型(Actual Type)。

所有依賴靜態型別來定位方法執行版本的分派動作,稱為靜態分派。 靜態分派的典型應用是方法重載,其發生在編譯階段,因此確定靜態分派的動作實際上不是由虛擬機器來執行的。

方法的接收者(Reciever) 和方法的參數統稱為方法的宗量,根據分派基於多少種宗量,可以將分派劃分為單分派和多分派兩種。

在靜態分派的時候,選擇目標方法的依據有兩點,一是靜態型別是Base還是Child,二是方法的參數型別。因此,靜態分派是多分派。

接下來,我們來看看「第1段輸出」程式碼所產生的指令。透過javap -v App.class指令得出以下結果,可以看到第18和第31行兩條指令的符號引用,和上述分析一致:child的靜態型別是Base,所以選擇Base類別的方法;透過無參數和Character類型,分別決定是具體哪個方法版本。

但最終兩者的行為不一樣,child.foo() 呼叫的是靜態型別Base的foo(),而child.bar(new Character('C ')) 則是呼叫實際類型Child的方法。

原因就是出在兩條指令不一樣:invokestatic和invokevirtual

#在Java虛擬機裡面提供了5條方法呼叫字節碼指令:

invokestatic:調用靜態方法

invokespecial:呼叫實例建構器方法、私有方法與父類別方法

invokevirtual: 呼叫所有的虛擬方法

#invokeinterface:呼叫介面方法,會在運行時再確定一個實現此介面的物件

invokedynamic:先在運行時動態解析出調用點限定符所引用的方法,然後再執行該方法,在此之前的4條調用指令,分派邏輯是固化在Java虛擬機器內部的,而invokedynamic是由使用者設定的引導方法決定的。

具體原因是不同的指令在下一階段(類別載入的解析)的行為不一樣,暫時先放到一邊,我們再看看第2段輸出的指令。

可以看出,在靜態分派時,是根據傳入方法的參數的靜態型別來決定呼叫的方法版本, 雖然有baz(Integer)的方法,但傳入的參數integer的靜態型別是Object,所以呼叫了baz(Object)。

再來看看第3段輸出的指令,我們知道符號引用肯定還是Base類別裡的方法(儘管Child類別裡有參數一樣的bar(char c) 方法),但Base裡沒有一模一樣參數(char類型) 的方法,不會報錯嗎?會呼叫哪個方法呢?

原來,編譯器雖然能確定方法的重載版本,但在很多情況下這個重載版本並不是“唯一的”,往往只能確定一個“更加合適”的版本。

· 類別載入之解析

 解析階段是虛擬機器將常數池內的符號引用替換為直接引用的過程。

只要能被invokestatic和invokespecial指令呼叫的方法,都可以在解析階段中確定唯一的呼叫版本,符合這個條件的有靜態方法,私有方法,實例構造器,父類方法4類,它們在類別載入的時候就會把符號引用解析為該方法的直接引用。這些方法可以稱為非虛方法,其他方法稱為非虛方法(除了final方法)。

final修飾的方法,雖然是使用invokevirtual指令來調用,但由於它無法被覆寫,沒有其他版本,因此也是非虛方法。

回到第1段輸出,child.foo()是invokestatic指令,那麼在解析階段,就會替換成直接引用,具體的類別也就確定下來了,因此呼叫的是靜態型別Base.foo( )。

而child.bar(new Character('C')) 是invokevirtual, 在這個階段可以決定呼叫的方法簽名,但還不能確定方法的接收者的實際類型。它將由動態分派來完成確定。 由於只有一個宗量影響,因此動態分派是單分派。

方法接收者的實際型別在下一階段決定。

· 運行期的方法呼叫

#在運行期根據實際類型決定方法執行版本的分派過程稱為動態分派。

最終輸出結果是:

#

以上是Java中靜態分派和動態分派的介紹(程式碼範例)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文轉載於:博客园。如有侵權,請聯絡admin@php.cn刪除
JVM中的類加載程序子系統如何促進平台獨立性?JVM中的類加載程序子系統如何促進平台獨立性?Apr 23, 2025 am 12:14 AM

類加載器通過統一的類文件格式、動態加載、雙親委派模型和平台無關的字節碼,確保Java程序在不同平台上的一致性和兼容性,實現平台獨立性。

Java編譯器會產生特定於平台的代碼嗎?解釋。Java編譯器會產生特定於平台的代碼嗎?解釋。Apr 23, 2025 am 12:09 AM

Java編譯器生成的代碼是平台無關的,但最終執行的代碼是平台特定的。 1.Java源代碼編譯成平台無關的字節碼。 2.JVM將字節碼轉換為特定平台的機器碼,確保跨平台運行但性能可能不同。

JVM如何處理不同操作系統的多線程?JVM如何處理不同操作系統的多線程?Apr 23, 2025 am 12:07 AM

多線程在現代編程中重要,因為它能提高程序的響應性和資源利用率,並處理複雜的並發任務。 JVM通過線程映射、調度機制和同步鎖機制,在不同操作系統上確保多線程的一致性和高效性。

在Java的背景下,'平台獨立性”意味著什麼?在Java的背景下,'平台獨立性”意味著什麼?Apr 23, 2025 am 12:05 AM

Java的平台獨立性是指編寫的代碼可以在任何安裝了JVM的平台上運行,無需修改。 1)Java源代碼編譯成字節碼,2)字節碼由JVM解釋執行,3)JVM提供內存管理和垃圾回收功能,確保程序在不同操作系統上運行。

Java應用程序仍然可以遇到平台特定的錯誤或問題嗎?Java應用程序仍然可以遇到平台特定的錯誤或問題嗎?Apr 23, 2025 am 12:03 AM

Javaapplicationscanindeedencounterplatform-specificissuesdespitetheJVM'sabstraction.Reasonsinclude:1)Nativecodeandlibraries,2)Operatingsystemdifferences,3)JVMimplementationvariations,and4)Hardwaredependencies.Tomitigatethese,developersshould:1)Conduc

雲計算如何影響Java平台獨立性的重要性?雲計算如何影響Java平台獨立性的重要性?Apr 22, 2025 pm 07:05 PM

云计算显著提升了Java的平台独立性。1)Java代码编译为字节码,由JVM在不同操作系统上执行,确保跨平台运行。2)使用Docker和Kubernetes部署Java应用,提高可移植性和可扩展性。

Java的平台獨立性在廣泛採用中扮演著什麼角色?Java的平台獨立性在廣泛採用中扮演著什麼角色?Apr 22, 2025 pm 06:53 PM

Java'splatformindependenceallowsdeveloperstowritecodeonceandrunitonanydeviceorOSwithaJVM.Thisisachievedthroughcompilingtobytecode,whichtheJVMinterpretsorcompilesatruntime.ThisfeaturehassignificantlyboostedJava'sadoptionduetocross-platformdeployment,s

容器化技術(例如Docker)如何影響Java平台獨立性的重要性?容器化技術(例如Docker)如何影響Java平台獨立性的重要性?Apr 22, 2025 pm 06:49 PM

容器化技術如Docker增強而非替代Java的平台獨立性。 1)確保跨環境的一致性,2)管理依賴性,包括特定JVM版本,3)簡化部署過程,使Java應用更具適應性和易管理性。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

Safe Exam Browser

Safe Exam Browser

Safe Exam Browser是一個安全的瀏覽器環境,安全地進行線上考試。該軟體將任何電腦變成一個安全的工作站。它控制對任何實用工具的訪問,並防止學生使用未經授權的資源。

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SAP NetWeaver Server Adapter for Eclipse

SAP NetWeaver Server Adapter for Eclipse

將Eclipse與SAP NetWeaver應用伺服器整合。

SublimeText3 Linux新版

SublimeText3 Linux新版

SublimeText3 Linux最新版