天天看點

Java中的綁定

Java中的綁定

1 前言

做了一次筆試題,發現了一題問答題,是關于

Java

綁定的:

Java中的綁定

當時做的時候是完全不會的。。。

于是這裡補上一篇

Java

綁定的文章。

2 綁定

首先來了解一下綁定的概念。綁定是指一個方法的調用與方法所在的類關聯起來。

很抽象吧,舉個例子,如果父類與子類存在同名方法,子類對父類方法進行了重寫,那麼就需要綁定來區分調用的到底是父類的方法還是子類的方法。相對簡單的一種了解是,綁定是一個方法的調用與調用這個方法的類連接配接在一起的過程。

而綁定具體又可以分為:

  • 靜态綁定:就是程式執行前,方法已經被綁定,可以簡單了解成編譯期綁定
  • 動态綁定:在運作時根據具體對象的類型進行綁定,通過一些機制去運作時判斷對象的類型,并分别調用适當的方法

3 靜态綁定

靜态綁定也叫前期綁定、編譯期綁定,在程式運作之前,也就是編譯時期

JVM

能夠确認方法由誰調用,這種機制就叫靜态綁定。

如果一個方法由

private

static

final

任意一個關鍵字修飾,那麼這個方法就是靜态綁定的,原因很簡單,因為:

  • private

    修飾的方法,無法由本類以外的類調用,也就是調用者隻能是該類
  • static

    修飾的方法,通過

    類名.方法名

    進行調用,也可以唯一确定了調用的類
  • final

    修飾的方法,不能被子類進行重寫,在編譯期就能确定了調用的類

這三個關鍵字修飾的方法,都可以在編譯時期就能唯一确定了調用的類,不存在子類調用的問題,是以使用靜态綁定,而不是動态綁定。

4 動态綁定

動态綁定就是運作時根據對象的類型進行綁定,簡單來說,

JVM

在運作時期決定由哪個對象調用的過程稱為動态綁定。

比如:

public class Main {
    public static void main(String[] args){
        A b = new B();
        b.print();
    }
}

class A{
    public void print(){
        System.out.println("A");
    }
}

class B extends A{
    @Override
    public void print(){
        System.out.println("B");
    }
}
           

由于B類繼承了A類,是以建立對象的時候:

A b = new B();
           

編譯期并不知道b真正引用的是A類還是B類,在運作的時候才知道b是一個A類對象,但是指向了B類的引用。

Java

中,所有的非

final

private

static

的方法都是動态綁定的,因為隻要繼承了就能重寫。

5 差別

  • 發生時期:靜态綁定發生在編譯時期,動态綁定發生在運作時期
  • 靈活性:動态綁定的靈活性要比靜态綁定高,因為靜态綁定在編譯的時期就确定了,而動态綁定在編譯的時候并不知道是調用哪一個類的方法
  • 速度:靜态綁定調用方法的速度要快于動态綁定,因為靜态綁定可以直接調用,而動态綁定需要去搜尋方法表

6 動态綁定的過程

在了解動态綁定的過程之前,先了解一些前置知識。

6.1 方法調用

Java

中的方法調用有兩類:

  • 靜态方法調用
  • 動态方法調用

而方法調用的指令有四個,分别是:

  • invokestatic

  • invokespecial

  • invokevirtual

  • invokeinterface

前兩個是靜态綁定的,而後兩個是動态綁定的。

6.2 方法表

方法表是位元組碼檔案的一部分,每個類都有一個方法表,方法表是為

invokevirtual

以及

invokeinterface

指令服務的。由于

Java

中的類都繼承于

Object

,是以,在預設情況下,所有類的方法表中都有

Object

的方法,如果重寫了其中的方法,就會改變其中的描述符。比如,

Object

類的方法表可以簡單了解如下:

Java中的綁定

而加載了A類的位元組碼後,因為A類并沒有重寫任何的

Object

方法,是以隻是添加了A類本身的方法:

Java中的綁定

而加載了B類的位元組碼後,因為重寫了

print()

,是以方法表如下:

Java中的綁定

6.3 具體過程

了解了前置知識後看具體過程就會相對簡單一點了,動态綁定的過程可以分為三步:

  • 虛拟機提取對象實際類型的方法表:

    JVM

    擷取到對象的實際類型後,再擷取該類型的方法表
  • 虛拟機搜尋方法簽名:當調用

    b.print()

    時,通過方法表發現實際方法是

    B.print()

  • 調用方法:調用

    B.print()

7 參考

  • StackOverflow-How does dynamic binding happens in JVM?
  • 部落格園-Java多态實作原理