Java前期繫結與後期繫結
阿新 • • 發佈:2018-12-13
在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程式碼首先會通過javac編譯成位元組碼檔案,此時程式碼並沒有放進記憶體。當需要類載入的時候,JVM會執行載入類的全過程,包括- 載入
- 驗證
- 準備
- 解析
- 初始化
動態繫結發生在載入類的解析階段。
現在假設當前程式碼所處的類為D,要把一個從未解析過的符號引用N(可以理解為程式碼中的 f,當然這並不準確)解析為一個類或介面C(可以理解為程式碼裡的Son物件)的直接引用(參考深入理解Java虛擬機器第二版),步驟如下:
- 如果C不是陣列型別,虛擬機器將會把代表N的全限定名傳遞給D的類載入器去載入這個類C。如果載入過程出現任何異常,則解析失敗。
- 如果C是陣列型別,並且陣列元素型別為物件,即N的描述符是類似 “[Ljava/lang/Integer” 的形式,按照第一點的規則載入陣列元素型別.,接著由虛擬機器生成一個代表此陣列維度和元素的陣列物件。
- 如果上面步驟沒有出現異常,則C在虛擬機器中已經成為一個有效的類或介面,之後會進行符號引用驗證,確認D是否具備對C的訪問許可權,否則丟擲異常。
- 符號引用:符號引用以一組符號來描述所引用的目標,符號可以是任何形式的字面量,只要使用時能無歧義地定位到目標即可。
- 全限定名:如果一個類叫 Hello,所在的包為 com.lslxy,則其全限定名為 com/lslxy/hello
- 簡單名稱:沒有型別和引數修飾的方法或者欄位名稱
- 描述符:欄位的資料型別、方法的引數列表(包括數量,型別及順序)和返回值
- 方法簽名:Java程式碼中的方法簽名包括方法名稱、引數順序和引數型別,位元組碼中的方法簽名包括方法名稱、引數順序、引數型別、返回值、受查異常表