1. 程式人生 > >Java前期繫結與後期繫結

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會執行載入類的全過程,包括
  • 載入
  • 驗證
  • 準備
  • 解析
  • 初始化

動態繫結發生在載入類的解析階段。

現在假設當前程式碼所處的類為D,要把一個從未解析過的符號引用N(可以理解為程式碼中的 f,當然這並不準確)解析為一個類或介面C(可以理解為程式碼裡的Son物件)的直接引用(參考深入理解Java虛擬機器第二版),步驟如下:

  1. 如果C不是陣列型別,虛擬機器將會把代表N的全限定名傳遞給D的類載入器去載入這個類C。如果載入過程出現任何異常,則解析失敗。
  2. 如果C是陣列型別,並且陣列元素型別為物件,即N的描述符是類似 “[Ljava/lang/Integer” 的形式,按照第一點的規則載入陣列元素型別.,接著由虛擬機器生成一個代表此陣列維度和元素的陣列物件。
  3. 如果上面步驟沒有出現異常,則C在虛擬機器中已經成為一個有效的類或介面,之後會進行符號引用驗證,確認D是否具備對C的訪問許可權,否則丟擲異常。
常見概念解釋:
  • 符號引用:符號引用以一組符號來描述所引用的目標,符號可以是任何形式的字面量,只要使用時能無歧義地定位到目標即可。
  • 全限定名:如果一個類叫 Hello,所在的包為 com.lslxy,則其全限定名為 com/lslxy/hello
  • 簡單名稱:沒有型別和引數修飾的方法或者欄位名稱
  • 描述符:欄位的資料型別、方法的引數列表(包括數量,型別及順序)和返回值
  • 方法簽名:Java程式碼中的方法簽名包括方法名稱、引數順序和引數型別,位元組碼中的方法簽名包括方法名稱、引數順序、引數型別、返回值、受查異常表