java學習筆記11——編譯時類型和運行時類型

編譯時類型和運行時類型:  

       Java的引用變量有兩個類型,一個是編譯時類型,一個是運行時類型,編譯時類型由聲明該變量時使用的類型決定,運行時類型由實際賦給該變量的對象決定。如果編譯時類型和運行時類型不一致,會出現所謂的多態。因爲子類其實是一種特殊的父類,因此java允許把一個子類對象直接賦值給一個父類引用變量,無須任何類型轉換,或者被稱爲向上轉型,由系統自動完成。

       引用變量在編譯階段只能調用其編譯時類型所具有的方法,但運行時則執行它運行時類型所具有的方法,因此,編寫Java代碼時,引用變量只能調用聲明該變量所用類裏包含的方法。與方法不同的是,對象的屬性則不具備多態性。通過引用變量來訪問其包含的實例屬性時,系統總是試圖訪問它編譯時類所定義的屬性,而不是它運行時所定義的屬性。

 

前期綁定和後期綁定(動態綁定、運行時綁定):

綁定:將一個方法調用同方法主體關聯起來叫做綁定

        前期綁定:在程序執行之前進行綁定(如果有的話,由編譯器和連接器完成),前期綁定是面向過程程序設計語言中默認的綁定方式,例如,C語言只有一種方法調用,那就是前期綁定。

      後期綁定:就是在程序運行時根據對象的類型進行綁定,也叫作動態綁定或運行時綁定。

        注意:Java中除了static和final方法(private方法屬於final方法,因爲類中的private方法被隱式指定爲final方法)之外,其他方法都是後期綁定。這意味着通常不必判定是否該進行後期綁定,因爲它是自動發生的。

下面舉例說明:

1.子類方法覆蓋父類方法( 子類重寫父類中的方法,調用子類中的方法


  1. class Father{  
  2.       public void method(){  
  3.           System.out.println("父類方法:"+this.getClass());  
  4.       }  
  5.  }  
  6.  public class Son extends Father{  
  7.     public void method(){  
  8.           System.out.println("子類方法:"+this.getClass());  
  9.       }  
  10.     public static void main(String[] args){  
  11.         Father instance = new Son();  
  12.         instance.method();  
  13.     }  
  14. }  

  1. 運行結果:<pre name="code" class="java" style="font-size:14px; line-height:26px"><pre name="code" class="java" style="font-size:14px; line-height:26px">子類方法:class Son</pre><br></pre>  
 2. 子類沒有重寫父類中的方法,所以到父類中尋找相應的方法

  1. class Father{  
  2.       public void method(){  
  3.           System.out.println("父類方法:"+this.getClass());  
  4.       }  
  5.  }  
  6.  public class Son extends Father{  
  7. <span style="white-space:pre">  </span>public static void main(String[] args){  
  8.         Father instance = new Son();  
  9.         instance.method();  
  10.     }  
  11. }  

  1. 運行結果: 父類方法: class Son  

3.動態綁定只是針對對象的方法,對於屬性無效。因爲屬性不能被重寫

  1. class Father{  
  2.      public String name = "Father'name";  
  3.         
  4.  }  
  5.   
  6.  public class Son extends Father{  
  7.     public String name = "Son'name";  
  8.   
  9.     public static void main(String[] args){  
  10.         Father instance = new Son();  
  11.         System.out.println(instance.name);  
  12.     }  
  13. }  

  1. 運行結果:<pre name="code" class="java" style="color:rgb(51,51,51); line-height:23px">Father'name</pre>  
這裏還可以從另外一個方面來說明:如果將Father類的

  1. public String name = "Father'name";  
修改爲

  1. private String name = "Father'name";  

  1. 那麼編譯器將報錯:  錯誤: name可以在Father中訪問private  
  2. <span style="white-space:pre">      </span>System.out.println(instance.name);  

  1. 說明在<pre name="code" class="java" style="color:rgb(51,51,51); line-height:23px">               System.out.println(instance.name);這行代碼執行時,訪問的是父類的 name屬性,而該屬性被聲明爲private,所以無法訪問,因而報錯!</pre>  

下面在分析一個例子:

  1. class A  
  2. {  
  3. int count = 20;  
  4. }  
  5.   
  6. class B extends A  
  7. {  
  8. int count = 200;  
  9. }  
  10.   
  11. public class Test   
  12. {  
  13. public static void main(String[] args)   
  14. {  
  15. A a = new A();  
  16. System.out.println(a.count);  
  17. B b = new B();  
  18. System.out.println(b.count);  
  19. A ab = b;   //向上轉型  
  20. System.out.println(ab.count);  
  21. }  
  22. }  
  23.   
  24. 運行結果  :20  
  25.             200  
  26.             20    
結果分析:

 前兩行的輸出毫無疑問,問題在
        A ab = b;    
        System.out.println(ab.count);
的輸出是20,而不是200;在這之間我們可以用
        System.out.println(ab == b);   
來進行簡單的判斷,結果輸出爲 true ,說明 ab 和 b 兩個引用變量指向同一個實例,既然 ab 和 b 指向同一個實例,爲什麼輸出的是20不是200呢?原因在於:
  1.對於 class A 和class B來說,class B是class A的子類,由於子類的變量並不會覆蓋父類的變量,所以實際上在class B中是存在來兩個count,在這分別記作 A.count 和B.count ;
        2.雖然在 class B中存在A.count 和B.count ,但是究竟輸出那一個 count ,取決於該引用變量的聲明時類型(本文開頭紅色文字部分已經說明),此處 聲明時類型 是 class A,所以輸出 20 即A.count ,同理若改爲 B ab = b ;則輸出 200 即 B.count ;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章