重載與覆蓋區別

參考網址:http://xm-king.iteye.com/blog/765334

重載與覆蓋區別
     有時候,類的同一種功能有多種實現方式,到底採用那種實現方式,取決於調用者給定的參數。例如 雜技師能訓練動物,對於不同的動物有不同的訓練方式。

Java代碼  收藏代碼
  1. public void train (Dog dog){   
  2. //訓練小狗站立,排隊,做算數   
  3. }   
  4. public void train(Monkey monkey){   
  5. //訓練猴子騎自行車等   
  6. }   

 
      再如某個類的一個功能是比較兩個城市是否相同,一種方式是比較兩個城市的名字,一種是除了比較兩個城市的名字外,還要比較所在國家的名字。

Java代碼  收藏代碼
  1. publi boolean isSameCity (String city1,String city2){   
  2. return city1.equals(city2);   
  3. }   
  4. public boolean isSameCity(String city1,String city2,Stirng Country1,String Country2){   
  5. return isSameCity(city1,city2)&&Country1.equals(Country2);   
  6. }  

 

 

在例如 java.lang.Math 類的 max ()方法能夠從兩個數字中取出最大值,它有多種實現方式。

 

Java代碼  收藏代碼
  1. public static int max(int a,int b)   
  2. public static int max(long a, long b)   
  3. public static int max(float a,float b)   

  
      以下程序多次調用Math 類的max() 方法,運行時,Java 虛擬機先判斷給定參數的類型,然後決定到底執行哪個 max()方法。 
      // 參數爲 int 類型,因此執行max(int a, int b)方法 
      Math.max(1,2); 
      //參數爲 flloat 類型,因此執行 max(float a, float b) 方法 
      Math.max(1.0F,2.9F);

      對於類的方法(包括從父類中繼承的方法)如果有兩個方法的方法名相同,但參數不一致,那麼可以說,一個方法是另一個方法的重載方法。 
      重載方法滿足以下條件 
            方法名相同 
            方法的參數類型,個數,順序至少有一項不相同 
            方法的返回類型可以不相同 
            方法的修飾符可以不相同 
      在一個類中不允許定義另個方法名相同,並且參數簽名也完全相同的方法。假如存在這樣的兩個方法,Java 虛擬機 在運行時就無法決定到底執行哪個方法。參數簽名是指參數的類型,個數和順序。 
      例如 :

Java代碼  收藏代碼
  1. public class Sample {   
  2. public void amethod(int i,String s){} }   
  3. //下面哪個方法可以加入到 Sample 類中去?   
  4. public void amethod(String s,int i) //OK   
  5. public int amethod(int i,String s){return 0//NO   
  6. //不行,參數簽名和類中方法一樣   
  7. public void amethod(int i,String myString){} //NO   
  8. //不行,參數簽名和類中方法一樣   
  9. public void Amethod (int i,Sting s){} // OK   
  10. //可以,因爲 Amethod 和amethod 是兩個不同的方法名稱。   
  11. abstract void amethod( int i); //NO   

 
       儘管它的參數列和 類中方法參數不一樣,但是,此處的Sample 類不是抽象類,所以不能包括這個抽象方法。假如把Sample 類改爲抽象類,就能把這個方法加入到 Sample 類中了。 
(源碼) 

Java代碼  收藏代碼
  1. public boolean compareCity(String city1,String city2){   
  2. return city1.equals(city2);   
  3. }   
  4.      
  5. public int compareCity(String city1,String city2){   
  6.      
  7. if(city1.equals(city2)){   
  8. return 1;   
  9. }else{   
  10. return 0;   
  11. }   
  12. }   

 
編譯錯誤:

Java代碼  收藏代碼
  1. compareCity(java.lang.String,java.lang.String) is already defined   
  2. // compareCity(String ,String ) 方法已經被定義過   
  3. 作爲程序的入口 main()方法也可以被重載。   
  4. public static void main(String args[]){   
  5.   
  6. }   
  7.   
  8. public void main(String s,int i){} //可以   
  9.   
  10. private void main(int i,String myString []){} //可以   
  11.   
  12. public void main(String s)throws Exception{} //可以   
  13.   
  14. public final static int main(String args[]){} //不可以   
  15. 它已經和已有的 main ()方法有相同的簽名,因此不允許再加到這個類中來。   
  16. main(java.lang.String []) is already defined in Sample   

 


    方法覆蓋 
      假如有100個類,分別是 Sub1,Sub2,Sub3…….Sub100 ,它們的一個共同行爲是寫字,除了Sub1用腳寫字外,其他都用手寫字。可以抽象一個父類Base,它有一個表示寫字的方法 write(),那麼這個方法到底如何實現呢? 從儘可能提高代碼可重用性的角度看,write() 方法應該採取適用於大多數子類的實現方式,這樣就可以避免在大多數子類中重複定義 write()方法。因此Base 類的 write() 方法定義如下: 
public void write(){ // Base 類的 write() 方法 用手寫字} 
      由於 Sub1 類的寫字的實現方式與Base 類不一樣,因此在Sub1類中必須重新定義 write() 方法。 
      public void write(){// Sub1 類中的 write() 方法 // 用腳寫字}

      如果在子類中定義的一個方法,其名稱,返回類型及參數簽名正好與父類中某個方法的名稱,返回類型及參數簽名相匹配,那麼可以說,子類的方法覆蓋了父類的方法。 
覆蓋方法 必須滿足多種約束 
1)子類方法的名稱,參數簽名和返回類型必須與父類方法的名稱,參數簽名和返回類型一致 
例如,下列代碼將發生編譯錯誤 

Java代碼  收藏代碼
  1. public class Base{   
  2.    public void method(){……….}   
  3. }   
  4. public class Sub extends Base{   
  5.    public int method(){……………. return 0};// 編譯錯誤   
  6. }   

 
           method() in Simon.Sub cannot overrid method() in Simon.Base; 
       attempting to use incompatible return type 
       在Simon 包 Sub 中的方法不不能重寫(覆蓋) 在 Simon 包 Base類中的方法試圖用不匹配的返回類型,Java編譯器首先判斷Sub 類的 method()方法與 Base 類的 method() 方法的參數簽名。由於兩者一致,所以Java 編譯器就認爲 Sub 類 的 method() 方法試圖覆蓋父類的方法,既然如此,Sub 類的 method() 方法就必須和被覆蓋的方法具有相同的返回類型。 
以下代碼中子類覆蓋了父類的一個方法,然後又定義了一個重載方法,這是合法的。

Java代碼  收藏代碼
  1. public class Base{   
  2. public void method(){…………..}   
  3. }   
  4.   
  5.    public class Sub extends Base{   
  6.    public void method(){……….}//覆蓋 Base 類的method 方法   
  7.    public int mehod(int a){………return 0.} //重載method 方法   
  8. }   

 
2) 子類方法不能縮小父類方法的訪問權限。例如以下代碼中子類的 method() 方法是私用的,父類的 method()方法是公共的,子類縮小了 父類方法的訪問權限,這是無效的方法覆蓋,將導致編譯錯誤。

Java代碼  收藏代碼
  1. public class Base{   
  2. public void method(){…………..}   
  3. }   
  4.   
  5. public class Sub extends Base{   
  6. private void method(){……….}//覆蓋 Base 類的method 方法,但是縮小了 父類方法訪問權限   
  7. }   

 

      method() in Simon.Sub cannot override method() in Simon.Base; 
      attempting to assign weaker access privileges ; 
      was public Simon 包中 的 Sub 類 method()不能重寫、覆蓋 Simon 包中Base類的 method()方法。 試圖分配一個更弱的訪問權限原來是 public (現在卻是 private) 爲什麼子類方法不允許縮小父類方法的訪問權限呢?這時因爲假如沒有這個限制,將於Java 語言的多態機制發生衝突。Base base = new Sub() ;//base 變量被定義爲Base 類型,但引用 Sub 的實例。

       base.method(); Java 編譯器認爲以上是合法的代碼,但是在運行時,根據動態綁定規則,Java 虛擬機會調用base 變量所引用的Sub 實例的 method()方法,如果這個方法爲 private 類型,Java 虛擬機就沒有辦法訪問它.所以爲了避免這樣的矛盾,Java 語言不允許子類方法縮小父類中被覆蓋方法的權限。 
3)子類方法不能拋出比父類方法更多的異常。 
      子類方法拋出的異常必須和父類方法拋出的異常相同,或者子類方法拋出的異常類是父類拋出的異常類的子類。 
      假設異常類ExceptionSub1 和 ExceptionSub2 是 ExceptionBase 類的子類,則以下代碼是合法的。 

Java代碼  收藏代碼
  1. public class Base{   
  2. void method () throws ExceptionBase{}   
  3. }   
  4. public class Sub1 extends Base{   
  5. void method () throws ExceptionSub1{}   
  6. }   
  7. public class Sub2 extends Base{   
  8. void method () throws ExceptionSub1,ExceptionSub2   
  9. }   
  10. public class Sub3 extends Base{   
  11. void methos () throws ExceptionBase   
  12. }   
  13. //以下代碼不合法   
  14. public class Base{   
  15. void method() throws ExceptionSub1{ }   
  16. }   
  17. public class Sub1 extends Base{   
  18. void method() throws ExceptionBase{ } //編譯出錯   
  19. }   
  20. public class Sub2 extends Base{   
  21. void method() throws ExceptionSub1,ExceptionSub2{}//編譯出錯   
  22. }   

 
爲什麼子類不允許拋出比父類方法更多的異常呢?這時因爲假如沒有這個限制,將會與Java 語言的多態機制發生衝突。 

Java代碼  收藏代碼
  1. Base base = new Sub2() ;//base 變量被定義爲Base 類型,但引用Sub2 的實例。   
  2. try{   
  3.    base.method();   
  4. }catch(ExceptionSub1 e){……..} //僅僅描述ExceptionSub1 異常   

 
      Java 編譯器認爲以上是合法的代碼。但在運行時,根據動態綁定規則,Java虛擬機會調用 base 變量所引用的Sub2 實例的 method() 方法。 假如 Sub2 實例的 method() 方法拋出 ExceptionSub2 異常,由於該異常沒有被捕獲,將導致程序異常終止。 
4)方法覆蓋只存在於子類和父類(包括直接父類和間接父類)之間,在同一個類中方法只能被重載,不能被覆蓋。 
5)父類的靜態方法不能被子類覆蓋爲非靜態方法。

例如:

Java代碼  收藏代碼
  1. public class Base {   
  2. public static void method(){}   
  3. }   
  4. public class Sub extends Base{   
  5. public void method(){} //編譯錯誤   
  6. }   

 
      method () in Simon.Sub cannot override method() in Simon.Base; 
      Overridden method is static
 
      在包Simon ,Sub類中的 method() 方法不能夠重寫 Base 類中的 method()方法。 被重寫的方法是靜態的。 
6)子類可以定義與父類的靜態方法同名的靜態方法,以便在子類中隱藏父類的靜態方法。 
      在編譯時,子類定義的靜態方法也必須滿足與方法覆蓋類似的約束: 
      方法的參數簽名一致,返回類型一致,不能縮小父類方法的訪問權限,不能拋出更多的異常。以下代碼是合法的。 
public class Base { 
static int method(int a)throws BaseException{ return 0 } 

public class Sub extends Base{ 
public static int method(int a)throws SubException{ return 0} } 
子類隱藏父類的靜態方法和子類覆蓋父類的實例方法,這兩者的區別就在於:運行時,JVM把靜態方法和所屬的類綁定,而把實例方法和所屬的實例綁定。 

Java代碼  收藏代碼
  1. Base.java   
  2. package Simon;   
  3.     
  4. public class Base{   
  5.      
  6. void method(){   
  7. System.out.println("method of Base !");//實例方法   
  8. }   
  9. static void staticMethod(){   
  10. System.out.println("static method of Base !");//靜態方法   
  11. }   
  12. }   
  13. Sub.java   
  14. package Simon;   
  15.      
  16. public class Sub extends Base{   
  17.      
  18. void method(){ //覆蓋父類實例方法 method()   
  19. System.out.println("method of Sub !");   
  20. }   
  21.     
  22. static void staticMethod(){ //隱藏父類的靜態方法 staticMethod ()   
  23. System.out.println("static method of Sub !");   
  24. }   
  25. public static void main(String args[]){   
  26.   
  27.    Base sub1 = new Sub(); //sub1 變量 被聲明爲 Base 類型,引用Sub 實例   
  28. sub1.method() ;//打印 method of Sub !   
  29. sub1.staticMethod() ;//打印 static method of Base    
  30.    Sub sub2 = new Sub(); //sub2 變量 被聲明爲 Sub 類型,引用 Sub 實例   
  31. sub2.method(); // 打印 method of Sub   
  32.    sub2.staticMethod(); // 打印 static method of Sub   
  33. }   
  34.    }   

 
        引用變量sub1 和 sub2 都引用 Sub 類的實例,java 虛擬機在執行 sub1.method() 和 sub2.method() 時,都調用Sub 實例的 method()方法,此時父類Base 實例方法 method() 被子類覆蓋。 
引用變量sub1 被聲明爲Base 類型,java 虛擬機在執行 sub1.staticMethod()時,調用Base 類的staticMethod()方法,可見父類Base 類的靜態方法staticMethod ()不能爲被子類覆蓋。 
引用變量 sub2 被聲明爲 Sub 類型,java 虛擬機在執行sub2.staticMethod()時,調用Sub 類的 staticMethod 方法,Base 類的staticMethod()方法隱藏。 
7)父類的非靜態方法不能被子類覆蓋爲靜態方法。 
以下代碼編譯有錯誤 

Java代碼  收藏代碼
  1. public class Base {   
  2.    public void method(){}   
  3. }   
  4.    public class Sub extends Base{   
  5. public static void method(){} //編譯錯誤   
  6. }   

 
method () in Simon.Sub cannot override method() in Simon.Base; 
overriding method is static .

8)父類的私有方法不能被子類覆蓋 
      子類方法覆蓋父類方法的前提是:子類必須能繼承父類的特定方法。 
      由於父類中的某個私有方法無法被子類訪問,繼承,所以也不存在被子類覆蓋的關係。 
示例: 

Java代碼  收藏代碼
  1. Base.java   
  2. package Simon;   
  3.     
  4. public class Base{   
  5.     
  6. private String showMe(){   
  7. return "Base";   
  8. }   
  9.   
  10. public void print(){   
  11. System.out.println(showMe());//到底調用哪一個showMe(),是Base 類還是Sub類中的。   
  12. }   
  13. }   
  14. Sub.java   
  15. package Simon;   
  16.     
  17. public class Sub extends Base{   
  18.   
  19. public String showMe(){   
  20. return "Sub";   
  21. }   
  22.     
  23. public static void main(String args[]){   
  24.     
  25. Sub sub = new Sub();   
  26.     
  27. sub.print();   
  28. }   
  29. }   
  30.     

 
執行以上Sub 類的 main() 方法,會打印出結果 Base ,這時因爲print()方法在Base 類中定義,因此print()方法會調用在Base 類中定義的 private 類型的 showMe() 方法 
如果把Base類的showMe() 方法改爲 public 類型,其他代碼不變 
public String showMe(){ 
return "Base"; 

在執行以上Sub 類的main ()方法,會打印出 Sub,這是因爲此時Sub 類的 showMe() 方法覆蓋了 Base 類的showMe() 方法。因此儘管print()方法在Base 類中定義,JVM 還是會調用當前Sub 實例的 ShowMe 方法。 
9)父類的抽象方法可以被子類通過兩種途徑覆蓋掉,一是子類實現父類的抽象方法,二是子類重新聲明父類的抽象方法。 

Java代碼  收藏代碼
  1. public abstract class Base{   
  2. abstract void method1();   
  3. abstract void method2   
  4. }   
  5. public abstract class Sub extends Base{   
  6. public void method(){…………}//實現method1方法,擴大訪問權限   
  7. public abstract void method2();//重新聲明 method2 方法,僅僅擴大訪問權限,但不實現。   
  8. }   

 
      狹義的理解,覆蓋僅指子類覆蓋父類的具體方法,即非抽象方法,在父類中提供了方法的默認實現方式,而子類採用不同的實現方式。 
例如,以下代碼編譯會有誤。 

Java代碼  收藏代碼
  1. public abstract class Base{   
  2.     
  3. abstract void method1();   
  4. abstract void method2();   
  5. }   
  6. public abstract class Sub extends Base{   
  7.     
  8. private void method(){//編譯有誤,不能縮小訪問權限   
  9.     
  10. System.out.println("method test!");   
  11. }   
  12.     
  13. private abstract void method2();//編譯有誤,不能縮小訪問權限,且會出現以下錯誤   
  14.   
  15. }   
  16. illegal combination of modifiers : abstract and private   
  17. abstract 不能和 private 關鍵字一起使用。抽象方法必須需要被實現,但是 private 是私有的,外界無法訪問,所以不行。   
  18. 10)父類的非抽象方法可以被覆蓋爲抽象方法   
  19. public class Base{   
  20. void methos();   
  21. }   
  22. public abstract class Sub extends Base{   
  23. public abstract void method(); //合法   
  24. }   
  25.   
  26.    

 

 

Java 方法重載 (OverLoad)與方法覆蓋(Override)的區別 
       方法覆蓋和方法重載具有以下相同點 
             都要求方法名相同 
             都可以用於抽象方法和非抽象方法之間 
      不同點 
            方法覆蓋要求參數簽名必須一致,而方法重載要求參數簽名必須不一致 
            方法覆蓋返回類型必須一致,方法重載對此不做限制 
            方法覆蓋只能用於子類覆蓋父類的方法,方法重載用於同一個類的所有方法(包括從父類中繼承而來的方法) 
            方法覆蓋對方法的訪問權限和拋出的異常有特殊的要求,而方法重載在這方面沒有任何限制 
            父類的一個方法只能被子類覆蓋一次,而一個方法在所在類中可以被重載多次

 



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