方法調用
- 方法調用
- 靜態鏈接
- 動態鏈接
- 案例
- 虛方法與非虛方法
- 虛方法(Virtual Method)
- 非虛方法(Non-Virtual Method)
- 方法返回地址
方法調用
我們編寫Java程序的時候,我們自己寫的類通常不僅僅是調用自己本類的方法。調用別的類的方法的時候,從字節碼的角度,我們調用別的類的方法,字節碼里面存儲的是別的類的符號引用。
但是JVM運行的時候,我們需要一個機制去把這個符號,轉化成實際的引用的類方法的地址,這樣我們運行的時候,才能夠找到要調用的方法。
在JVM中,將符號引用轉化為調用方法的直接引用與方法的綁定機制有關,方法的綁定機制有兩種:
- 靜態綁定
- 動態綁定
靜態鏈接
Java源代碼轉化成字節碼文件裝載到JVM區域的時候,如果被調用的類的目標方法,編譯期間就可以確定下來的話,而且運行期間不會變。這時候,我們可以將調用方法的符號引用直接轉化成目標方法的直接引用,這種情況就是靜態鏈接或者早期綁定。
動態鏈接
被調用的方法如果編譯期間無法確定下來,這種情況,程序只能夠在運行期間將調用方法的符號引用轉化成直接引用,這種情況就叫動態鏈接或者晚期綁定。
案例
class Student{public void study() {System.out.println("begin study");}
}interface Play{public void play();
}class JuniorStudent extends Student implements Play{@Overridepublic void play() {System.out.println("JuniorStudent play");}
}class MiddleStudent extends Student implements Play{@Overridepublic void play() {System.out.println("MiddleStudent play");}
}public class LinkTest {public void play(Play play) {play.play();}public void study(Student student) {student.study();}
}
虛方法與非虛方法
JVM的實現機制
- 虛方法調用
JVM使用虛方法表(vtable)實現動態分派。每個類維護一個虛方法表,記錄方法的實際入口地址。調用時根據對象的實際類型查表,找到正確的方法實現。 - 非虛方法調用
直接通過符號引用在編譯期確定調用目標,無需運行時查找。
虛方法(Virtual Method)
虛方法是支持動態綁定(運行時綁定)的方法,具體調用的方法實現由對象的實際類型(運行時類型)決定。Java中,默認情況下,未被final、private或static修飾的實例方法都是虛方法。
特點
-
動態綁定:方法調用在運行時根據對象的實際類型確定。
-
支持多態:允許子類重寫(Override)父類方法,實現多態。
-
虛方法表(vtable):JVM通過虛方法表快速查找方法的實際實現。
class Animal {public void speak() { // 虛方法(可被重寫)System.out.println("Animal speaks");}
}class Dog extends Animal {@Overridepublic void speak() { // 重寫父類方法System.out.println("Dog barks");}
}public class Test {public static void main(String[] args) {Animal animal = new Dog();animal.speak(); // 輸出 "Dog barks"(動態綁定到Dog的speak方法)}
}
常見虛方法
-
普通實例方法(未被final、private、static修飾)。
-
接口的默認方法(default方法)。
-
抽象方法(abstract方法)。
非虛方法(Non-Virtual Method)
非虛方法是靜態綁定(編譯時綁定)的方法,調用的具體方法在編譯期就能確定,與對象的實際類型無關。這些方法無法被重寫,或不需要動態分派。
特點
-
靜態綁定:方法調用在編譯時確定。
-
無法被重寫:子類無法修改其行為。
-
性能更高:無需運行時查找方法表。
class Parent {public static void staticMethod() { // 非虛方法(靜態方法)System.out.println("Parent's static method");}private void privateMethod() { // 非虛方法(private方法)System.out.println("Parent's private method");}public final void finalMethod() { // 非虛方法(final方法)System.out.println("Parent's final method");}
}class Child extends Parent {// 嘗試重寫靜態方法(實際是隱藏,而非重寫)public static void staticMethod() {System.out.println("Child's static method");}// 無法重寫private方法和final方法
}public class Test {public static void main(String[] args) {Parent parent = new Child();parent.staticMethod(); // 輸出 "Parent's static method"(靜態綁定)}
}
常見的非虛方法
-
靜態方法(static):屬于類,調用時基于引用類型。
-
私有方法(private):僅在類內部可見,無法被重寫。
-
final方法:禁止子類重寫。
-
構造方法:隱式調用,無法被動態分派。
-
通過super調用的父類方法:直接指定父類實現。
方法返回地址
當一個方法開始執行之后,只有兩種可能:
- 正常結束,當前方法棧幀出棧, 返回上一個方法的棧幀;
- 異常結束,如果本方法沒有處理異常的方法,方法就會異常退出,不會給調用者提供任何返回值
無論是怎樣退出,在方法退出之后,都需要恢復到被調用之前的那個方法的棧幀的當時的狀態,程序才能正常往下執行。從棧的角度,方法退出,實際上是當前棧幀出棧,要恢復上層方法的局部變量表與操作數棧,如果有返回值,還需要把返回值壓入操作數棧,然后將程序計數器指向上層方法的下一條指令的地址。