一個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、多態是設計模式的基礎,能夠降低耦合性。