編譯時類型和運行時類型: java
Java的引用變量有兩個類型,一個是編譯時類型,一個是運行時類型,編譯時類型由聲明該變量時使用的類型決定,運行時類型由實際賦給該變量的對象決定。若是編譯時類型和運行時類型不一致,會出現所謂的多態。由於子類實際上是一種特殊的父類,所以java容許把一個子類對象直接賦值給一個父類引用變量,無須任何類型轉換,或者被稱爲向上轉型,由系統自動完成。this
引用變量在編譯階段只能調用其編譯時類型所具備的方法,但運行時則執行它運行時類型所具備的方法,所以,編寫Java代碼時,引用變量只能調用聲明該變量所用類裏包含的方法。與方法不一樣的是,對象的屬性則不具有多態性。經過引用變量來訪問其包含的實例屬性時,系統老是試圖訪問它編譯時類所定義的屬性,而不是它運行時所定義的屬性。spa
—— 以上摘自《瘋狂Java講義》設計
前期綁定和後期綁定(動態綁定、運行時綁定):code
綁定:將一個方法調用同方法主體關聯起來叫作綁定對象
前期綁定:在程序執行以前進行綁定(若是有的話,由編譯器和鏈接器完成),前期綁定是面向過程程序設計語言中默認的綁定方式,例如,C語言只有一種方法調用,那就是前期綁定。get
後期綁定:就是在程序運行時根據對象的類型進行綁定,也叫做動態綁定或運行時綁定。編譯器
注意:Java中除了static和final方法(private方法屬於final方法,由於類中的private方法被隱式指定爲final方法)以外,其餘方法都是後期綁定。這意味着一般沒必要斷定是否該進行後期綁定,由於它是自動發生的。編譯
下面舉例說明:程序設計
1.子類方法覆蓋父類方法( 子類重寫父類中的方法,調用子類中的方法)
class Father{ public void method(){ System.out.println("父類方法:"+this.getClass()); } } public class Son extends Father{ public void method(){ System.out.println("子類方法:"+this.getClass()); } public static void main(String[] args){ Father instance = new Son(); instance.method(); } }
運行結果:子類方法:class Son
2. 子類沒有重寫父類中的方法,因此到父類中尋找相應的方法
class Father{ public void method(){ System.out.println("父類方法:"+this.getClass()); } } public class Son extends Father{ public static void main(String[] args){ Father instance = new Son(); instance.method(); } }
運行結果: 父類方法: class Son
3.動態綁定只是針對對象的方法,對於屬性無效。由於屬性不能被重寫。
class Father{ public String name = "Father'name"; } public class Son extends Father{ public String name = "Son'name"; public static void main(String[] args){ Father instance = new Son(); System.out.println(instance.name); } }
運行結果:Father'name
這裏還能夠從另一個方面來講明:若是將Father類的
public String name = "Father'name";
那麼編譯器將報錯: 錯誤: name能夠在Father中訪問private 這行代碼執行時,訪問的是父類的 name屬性,而該屬性被聲明爲private,因此沒法訪問,於是報錯!
下面再分析一個例子:
class A { int count = 20; } class B extends A { int count = 200; } public class Test { public static void main(String[] args) { A a = new A(); System.out.println(a.count); B b = new B(); System.out.println(b.count); A ab = b; //向上轉型 System.out.println(ab.count); } } 運行結果 : 20 200 20
結果分析:
前兩行的輸出毫無疑問,問題在
A ab = b;
System.out.println(ab.count);
的輸出是20,而不是200;在這之間咱們能夠用
System.out.println(ab == b);
來進行簡單的判斷,結果輸出爲 true ,說明 ab 和 b 兩個引用變量指向同一個實例,既然 ab 和 b 指向同一個實例,爲何輸出的是20不是200呢?緣由在於:
一、對於 class A 和class B來講,class B是class A的子類,因爲子類的變量並不會覆蓋父類的變量,因此實際上在class B中是存在來兩個count,在這分別記做 A.count 和B.count ;
二、雖然在 class B中存在A.count 和B.count ,可是究竟輸出那一個 count ,取決於該引用變量的聲明時類型(本文開頭紅色文字部分已經說明),此處 聲明時類型 是 class A,因此輸出 20 即A.count ,同理若改成 B ab = b ;則輸出 200 即 B.count ;