手把手帶你深入解析靜態分派 & 動態分派原理

本文將會帶你瞭解行為方法分派,從而幫助你在行為分派時進行一些功能操作。具體將會講解行為分派的類型:靜態 & 動態行為分派,希望對大家有幫助。

手把手带你深入解析静态分派 & 动态分派原理 | 原力计划

作者 | Carson_Ho

本文系CSDN博文

手把手带你深入解析静态分派 & 动态分派原理 | 原力计划
手把手带你深入解析静态分派 & 动态分派原理 | 原力计划手把手带你深入解析静态分派 & 动态分派原理 | 原力计划

知識儲備

分派

定義:確定執行哪個方法的過程。

有些讀者會問,方法的執行不是取決於代碼設置中的執行對象嗎?為什麼還要選擇呢?答案是,若一個對象對應於多個方法時,就需要進行選擇。

讀者應該都想到了Java中的特性:多態,即重寫 & 重載。

此外還有分類,分類包括靜態分派 & 動態分派。下面我將詳細講解。

變量的靜態類型 & 動態類型

先看下面的代碼

public class Test {

static abstract class Human {

}

static class Man extends Human {

}

static class Woman extends Human {

}

// 執行代碼

public static void main(String[] args) {

Human man = new Man;

// 變量man的靜態類型 = 引用類型 = Human:不會被改變、在編譯器可知

// 變量man的動態類型 = 實例對象類型 = Man:會變化、在運行期才可知

}

}

即:

變量的靜態類型 = 引用類型 :不會被改變、在編譯器可知

變量的動態類型 = 實例對象類型 :會變化、在運行期才可知

下面,我將詳細講解Java中的分派類型:靜態分派 & 動態分派

手把手带你深入解析静态分派 & 动态分派原理 | 原力计划

靜態分派

定義

根據變量的靜態類型,進行方法分派的行為,即根據變量的靜態類型,確定執行哪個方法。這一般發生在編譯期,所以不由 Java 虛擬機來執行。

應用場景:方法重載(OverLoad)

實例說明:

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);

}

}

// 運行結果

hello,guy!

hello,guy!

根據上述的講解,大家應該明白運行結果的原因:

方法重載(OverLoad) = 靜態分派 = 根據變量的靜態類型確定執行(重載)哪個方法。

所以上述的方法執行時,是根據變量(Man、Woman)的靜態類型(Human)確定重載sayHello中參數為Human guy的方法,即sayHello(Human guy)

特別注意變量的靜態類型發生變化的情況,遇到這種情況時,可通過強制類型轉換改變變量的靜態類型。

Human man = new Man;

test.sayHello((Man)man);

// 強制類型轉換

// 此時man的靜態類型從 Human 變為 Man

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

靜態分派的優先級匹配問題

問題描述:

1、背景

現需要進行靜態分派。

2、問題

程序中沒有顯示指定靜態類型。

3、解決方案

程序會根據靜態類型的優先級,從而選擇優先的靜態類型進行方法分配。

實例說明

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');

}

}

// 運行結果

hello char

因為‘a’除了可代表字符串,還可代表數字97。因此當沒有最合適的sayHello(char arg)方式進行重載時,會選擇第二合適(第二優先級)的方法重載,即

hello int

因為‘a’除了可代表字符串,還可代表數字97。因此當沒有最合適的sayHello(char arg)方式進行重載時,會選擇第二合適(第二優先級)的方法重載,即sayHello(int arg)

總結:當沒有最合適的方法進行重載時,會選優先級第二高的的方法進行重載,如此類推。

  • 優先級順序為:char>int>long>float>double>Character>Serializable>Object>...

  • 其中...為變長參數,將其視為一個數組元素。變長參數的重載優先級最低。

  • 因為 char 轉型到 byte 或 short 的過程是不安全的,所以不會選擇參數類型為byte 或 short的方法進行重載,故優先級列表裡也沒有。

特別注意,上面講解的主要是基本數據類型的優先級匹配問題,若是引用類型,則根據繼承關係進行優先級匹配,注意只跟其編譯時類型(即靜態類型)相關。

手把手带你深入解析静态分派 & 动态分派原理 | 原力计划

動態分派

定義:根據變量的動態類型,進行方法分派的行為,即根據變量的動態類型確定執行哪個方法。

應用場景:方法重寫(Override)

實例說明

// 定義類

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;

}

}

// 運行結果

man say hello

woman say hello

// 原因解析

// 1. 方法重寫(Override) = 動態分派 = 根據 變量的動態類型 確定執行(重寫)哪個方法

// 2. 對於情況1:根據變量(Man)的動態類型(man)確定調用man中的重寫方法sayHello

// 3. 對於情況2:根據變量(Man)的動態類型(woman)確定調用woman中的重寫方法sayHello

特別注意,對於代碼中:

Human man = new Man;

man = new Woman;

man.sayHello;

// man稱為執行sayHello方法的所有者,即接受者。

  • invokevirtual指令執行的第一步 = 確定接受者的實際類型。

  • invokevirtual指令執行的第二步 = 將 常量池中 類方法符號引用 解析到不同的直接引用上。第二步即方法重寫(Override)的本質。

手把手带你深入解析静态分派 & 动态分派原理 | 原力计划

二者區別

手把手带你深入解析静态分派 & 动态分派原理 | 原力计划手把手带你深入解析静态分派 & 动态分派原理 | 原力计划


分享到:


相關文章: