一個Java 程式要經過編寫、編譯、執行三個步驟,其中編寫程式碼不在我們討論的範圍之內,那麼我們的重點自然就放在了編譯 和 執行這兩個階段,由於編譯和執行階段過程相當繁瑣,下面就我的理解來進行解釋:
Java程式從原始檔建立到程式執行要經過兩大步驟:
1、編譯時期是由編譯器將原始檔編譯成位元組碼的過程
2、位元組碼檔案由Java虛擬機器解釋執行
繫結
繫結就是一個方法的呼叫與呼叫這個方法的類連線在一起的過程被稱為繫結。
繫結分類
繫結主要分為兩種:
靜態繫結 和 動態繫結
繫結的其他叫法
靜態繫結 == 前期繫結 == 編譯時繫結
動態繫結 == 後期繫結 == 執行時繫結
為了方便區分: 下面統一稱呼為靜態繫結和動態繫結
靜態繫結
在程式執行前,也就是編譯時期JVM就能夠確定方法由誰呼叫,這種機制稱為靜態繫結
識別靜態繫結的三個關鍵字以及各自的理解
如果一個方法由private、Static、final任意一個關鍵字所修飾,那麼這個方法是前期繫結的
構造方法也是前期繫結
private:private關鍵字是私有的意思,如果被private修飾的方法是無法由本類之外的其他類所呼叫的,也就是本類所特有的方法,所以也就由編譯器識別此方法是屬於哪個類的
public class Person {
private String talk;
private String canTalk(){
return talk;
}
}
class Animal{
public static void main(String[] args) {
Person p = new Person();
// private 修飾的方法是Person類獨有的,所以Animal類無法訪問(動物本來就不能說話)
// p.canTalk();
}
}複製程式碼
final:final修飾的方法不能被重寫,但是可以由子類進行呼叫,如果將方法宣告為final可以有效的關閉動態繫結
public class Fruit {
private String fruitName;
final String eatingFruit(String name){
System.out.println("eating " + name);
return fruitName;
}
}
class Apple extends Fruit{
// 不能重寫final方法,eatingFruit方法只屬於Fruit類,Apple類無法呼叫
// String eatingFruit(String name){
// super.eatingFruit(name);
// }
String eatingApple(String name){
return super.eatingFruit(name);
}
}複製程式碼
static: static修飾的方法比較特殊,不用通過new出某個類來呼叫,由類名.變數名直接呼叫該方法,這個就很關鍵了,new 很關鍵,也可以認為是開啟多型的導火索,而由類名.變數名直接呼叫的話,此時的類名是確定的,並不會產生多型,如下程式碼:
public class SuperClass {
public static void sayHello(){
System.out.println("由 superClass 說你好");
}
}
public class SubClass extends SuperClass{
public static void sayHello(){
System.out.println("由 SubClass 說你好");
}
public static void main(String[] args) {
SuperClass.sayHello();
SubClass.sayHello();
}
}複製程式碼
SubClass 繼承SuperClass 後,在是無法重寫sayHello方法的,也就是說sayHello()方法是對子類隱藏的,但是你可以編寫"自己的"sayHello()方法,也就是子類SubClass 的sayHello()方法,由此可見,方法由static 關鍵詞所修飾,也是編譯時繫結
動態繫結
概念
在執行時根據具體物件的型別進行繫結
除了由private、final、static 所修飾的方法和構造方法外,JVM在執行期間決定方法由哪個物件呼叫的過程稱為動態繫結
如果把編譯、執行看成一條時間線的話,在執行前必須要進行程式的編譯過程,那麼在編譯期進行的繫結是前期繫結,在程式執行了,發生的繫結就是後期繫結
程式碼理解
public class Father {
void drinkMilk(){
System.out.println("父親喜歡喝牛奶");
}
}
public class Son extends Father{
@Override
void drinkMilk() {
System.out.println("兒子喜歡喝牛奶");
}
public static void main(String[] args) {
Father son = new Son();
son.drinkMilk();
}
}複製程式碼
Son類繼承Father類,並重寫了父類的dringMilk()方法,在輸出結果得出的是兒子喜歡喝牛奶。那麼上面的繫結方式是什麼呢?
上面的繫結方式稱之為動態繫結,因為在你編寫 Father son = new Son()的時候,編譯器並不知道son物件真正引用的是誰,在程式執行時期才知道,這個son是一個Father類的物件,但是卻指向了Son的引用,這種概念稱之為多型,那麼我們就能夠整理出來多型的三個原則:
1. 繼承
2.重寫
3.父類物件指向子類引用
也就是說,在Father son = new Son() ,觸發了動態繫結機制。
動態繫結的過程
- 虛擬機器提取物件的實際型別的方法表;
- 虛擬機器搜尋方法簽名;
- 呼叫方法。
動態繫結和靜態繫結的特點
靜態繫結
靜態繫結在編譯時期觸發,那麼它的主要特點是
1、編譯期觸發,能夠提早知道程式碼錯誤
2、提高程式執行效率
動態繫結
1、使用動態繫結的前提條件能夠提高程式碼的可用性,使程式碼更加靈活。
2、多型是設計模式的基礎,能夠降低耦合性。
參考: https://blog.csdn.net/zhangjk1993/article/details/24066085