天天看點

Java 前期綁定與後期綁定

在 Java 面向對象的三大特征封裝、繼承、多态中,多态對于剛接觸的人來說往往較難了解。了解它的原理有助于我們更深一步的認識。

我們知道,Java 中的多态表現為同一個行為具有多個不同表現形式,使得我們可以通過父類的引用指向子類的方法,比如下面這樣:

class Father {
    public void g() {
        System.out.println("father's g()");
    }
}
    
class Son extends Father {
    public void g() {
        System.out.println("son's g()");
    }
}
    
public class Polymorphic {
    public static void main(String[] args) {
        Father f = new Son();
        f.g();
    }
}      

輸出結果:

son's g()      

那程式是怎麼知道調用的是子類的方法呢?

綁定

綁定指的是一個方法的調用與方法所在的類(方法主體)關聯起來。對 Java 來說,綁定分為靜态綁定和動态綁定,或者叫做前期綁定和後期綁定。

  • 靜态綁定:在程式執行前方法已經被綁定(也就是說在編譯過程中就已經知道這個方法到底是哪個類中的方法),由編譯器實作。C就是典型的前期綁定。Java 中 final,static,private 修飾的方法和構造方法是前期綁定
  • 動态綁定:運作時根據具體對象的類型進行綁定。

在 Java 程式設計思想中,作者将多态稱作動态綁定;在其他一些地方,将方法重載(overload)稱為編譯時多态,方法覆寫(override)稱為運作時多态(​​編譯時與運作時​​)。大家隻要了解了原理即可,不必過分糾結于概念。

加載過程

Java 代碼首先會通過 javac 編譯成位元組碼檔案,此時代碼并沒有放進記憶體。當需要類加載的時候,JVM 會執行加載類的全過程,包括

  • 加載
  • 驗證
  • 準備
  • 解析
  • 初始化

動态綁定發生在加載類的解析階段。

  1. 如果C不是數組類型,虛拟機将會把代表N的全限定名傳遞給D的類加載器去加載這個類C。如果加載過程出現任何異常,則解析失敗。
  2. 如果C是數組類型,并且數組元素類型為對象,即N的描述符是類似 “[Ljava/lang/Integer”的形式,按照第一點的規則加載數組元素類型,接着由虛拟機生成一個代表此數組次元和元素的數組對象。
  3. 如果上面步驟沒有出現異常,則C在虛拟機中已經成為一個有效的類或接口,之後會進行符号引用驗證,确認D是否具備對C的通路權限,否則抛出異常。
  • 符号引用:符号引用以一組符号來描述所引用的目标,符号可以是任何形式的字面量,隻要使用時能無歧義地定位到目标即可。
  • 全限定名:如果一個類叫 Hello,所在的包為 com.lslxy,則其全限定名為 com/lslxy/hello
  • 簡單名稱:沒有類型和參數修飾的方法或者字段名稱
  • 描述符:字段的資料類型、方法的參數清單(包括數量,類型及順序)和傳回值
  • 方法簽名:Java 代碼中的方法簽名包括方法名稱、參數順序和參數類型,位元組碼中的方法簽名包括方法名稱、參數順序、參數類型、傳回值、受查異常表