參考網址:http://xm-king.iteye.com/blog/765334
重載與覆蓋區別
有時候,類的同一種功能有多種實現方式,到底採用那種實現方式,取決於調用者給定的參數。例如 雜技師能訓練動物,對於不同的動物有不同的訓練方式。
- public void train (Dog dog){
- //訓練小狗站立,排隊,做算數
- }
- public void train(Monkey monkey){
- //訓練猴子騎自行車等
- }
再如某個類的一個功能是比較兩個城市是否相同,一種方式是比較兩個城市的名字,一種是除了比較兩個城市的名字外,還要比較所在國家的名字。
- publi boolean isSameCity (String city1,String city2){
- return city1.equals(city2);
- }
- public boolean isSameCity(String city1,String city2,Stirng Country1,String Country2){
- return isSameCity(city1,city2)&&Country1.equals(Country2);
- }
在例如 java.lang.Math 類的 max ()方法能夠從兩個數字中取出最大值,它有多種實現方式。
- public static int max(int a,int b)
- public static int max(long a, long b)
- 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 虛擬機 在運行時就無法決定到底執行哪個方法。參數簽名是指參數的類型,個數和順序。
例如 :
- public class Sample {
- public void amethod(int i,String s){} }
- //下面哪個方法可以加入到 Sample 類中去?
- public void amethod(String s,int i) //OK
- public int amethod(int i,String s){return 0} //NO
- //不行,參數簽名和類中方法一樣
- public void amethod(int i,String myString){} //NO
- //不行,參數簽名和類中方法一樣
- public void Amethod (int i,Sting s){} // OK
- //可以,因爲 Amethod 和amethod 是兩個不同的方法名稱。
- abstract void amethod( int i); //NO
儘管它的參數列和 類中方法參數不一樣,但是,此處的Sample 類不是抽象類,所以不能包括這個抽象方法。假如把Sample 類改爲抽象類,就能把這個方法加入到 Sample 類中了。
(源碼)
- public boolean compareCity(String city1,String city2){
- return city1.equals(city2);
- }
- public int compareCity(String city1,String city2){
- if(city1.equals(city2)){
- return 1;
- }else{
- return 0;
- }
- }
編譯錯誤:
- compareCity(java.lang.String,java.lang.String) is already defined
- // compareCity(String ,String ) 方法已經被定義過
- 作爲程序的入口 main()方法也可以被重載。
- public static void main(String args[]){
- }
- public void main(String s,int i){} //可以
- private void main(int i,String myString []){} //可以
- public void main(String s)throws Exception{} //可以
- public final static int main(String args[]){} //不可以
- 它已經和已有的 main ()方法有相同的簽名,因此不允許再加到這個類中來。
- 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)子類方法的名稱,參數簽名和返回類型必須與父類方法的名稱,參數簽名和返回類型一致
例如,下列代碼將發生編譯錯誤
- public class Base{
- public void method(){……….}
- }
- public class Sub extends Base{
- public int method(){……………. return 0};// 編譯錯誤
- }
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() 方法就必須和被覆蓋的方法具有相同的返回類型。
以下代碼中子類覆蓋了父類的一個方法,然後又定義了一個重載方法,這是合法的。
- public class Base{
- public void method(){…………..}
- }
- public class Sub extends Base{
- public void method(){……….}//覆蓋 Base 類的method 方法
- public int mehod(int a){………return 0.} //重載method 方法
- }
2) 子類方法不能縮小父類方法的訪問權限。例如以下代碼中子類的 method() 方法是私用的,父類的 method()方法是公共的,子類縮小了 父類方法的訪問權限,這是無效的方法覆蓋,將導致編譯錯誤。
- public class Base{
- public void method(){…………..}
- }
- public class Sub extends Base{
- private void method(){……….}//覆蓋 Base 類的method 方法,但是縮小了 父類方法訪問權限
- }
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 類的子類,則以下代碼是合法的。
- public class Base{
- void method () throws ExceptionBase{}
- }
- public class Sub1 extends Base{
- void method () throws ExceptionSub1{}
- }
- public class Sub2 extends Base{
- void method () throws ExceptionSub1,ExceptionSub2
- }
- public class Sub3 extends Base{
- void methos () throws ExceptionBase
- }
- //以下代碼不合法
- public class Base{
- void method() throws ExceptionSub1{ }
- }
- public class Sub1 extends Base{
- void method() throws ExceptionBase{ } //編譯出錯
- }
- public class Sub2 extends Base{
- void method() throws ExceptionSub1,ExceptionSub2{}//編譯出錯
- }
爲什麼子類不允許拋出比父類方法更多的異常呢?這時因爲假如沒有這個限制,將會與Java 語言的多態機制發生衝突。
- Base base = new Sub2() ;//base 變量被定義爲Base 類型,但引用Sub2 的實例。
- try{
- base.method();
- }catch(ExceptionSub1 e){……..} //僅僅描述ExceptionSub1 異常
Java 編譯器認爲以上是合法的代碼。但在運行時,根據動態綁定規則,Java虛擬機會調用 base 變量所引用的Sub2 實例的 method() 方法。 假如 Sub2 實例的 method() 方法拋出 ExceptionSub2 異常,由於該異常沒有被捕獲,將導致程序異常終止。
4)方法覆蓋只存在於子類和父類(包括直接父類和間接父類)之間,在同一個類中方法只能被重載,不能被覆蓋。
5)父類的靜態方法不能被子類覆蓋爲非靜態方法。
例如:
- public class Base {
- public static void method(){}
- }
- public class Sub extends Base{
- public void method(){} //編譯錯誤
- }
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把靜態方法和所屬的類綁定,而把實例方法和所屬的實例綁定。
- Base.java
- package Simon;
- public class Base{
- void method(){
- System.out.println("method of Base !");//實例方法
- }
- static void staticMethod(){
- System.out.println("static method of Base !");//靜態方法
- }
- }
- Sub.java
- package Simon;
- public class Sub extends Base{
- void method(){ //覆蓋父類實例方法 method()
- System.out.println("method of Sub !");
- }
- static void staticMethod(){ //隱藏父類的靜態方法 staticMethod ()
- System.out.println("static method of Sub !");
- }
- public static void main(String args[]){
- Base sub1 = new Sub(); //sub1 變量 被聲明爲 Base 類型,引用Sub 實例
- sub1.method() ;//打印 method of Sub !
- sub1.staticMethod() ;//打印 static method of Base
- Sub sub2 = new Sub(); //sub2 變量 被聲明爲 Sub 類型,引用 Sub 實例
- sub2.method(); // 打印 method of Sub
- sub2.staticMethod(); // 打印 static method of Sub
- }
- }
引用變量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)父類的非靜態方法不能被子類覆蓋爲靜態方法。
以下代碼編譯有錯誤
- public class Base {
- public void method(){}
- }
- public class Sub extends Base{
- public static void method(){} //編譯錯誤
- }
method () in Simon.Sub cannot override method() in Simon.Base;
overriding method is static .
8)父類的私有方法不能被子類覆蓋
子類方法覆蓋父類方法的前提是:子類必須能繼承父類的特定方法。
由於父類中的某個私有方法無法被子類訪問,繼承,所以也不存在被子類覆蓋的關係。
示例:
- Base.java
- package Simon;
- public class Base{
- private String showMe(){
- return "Base";
- }
- public void print(){
- System.out.println(showMe());//到底調用哪一個showMe(),是Base 類還是Sub類中的。
- }
- }
- Sub.java
- package Simon;
- public class Sub extends Base{
- public String showMe(){
- return "Sub";
- }
- public static void main(String args[]){
- Sub sub = new Sub();
- sub.print();
- }
- }
執行以上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)父類的抽象方法可以被子類通過兩種途徑覆蓋掉,一是子類實現父類的抽象方法,二是子類重新聲明父類的抽象方法。
- public abstract class Base{
- abstract void method1();
- abstract void method2
- }
- public abstract class Sub extends Base{
- public void method(){…………}//實現method1方法,擴大訪問權限
- public abstract void method2();//重新聲明 method2 方法,僅僅擴大訪問權限,但不實現。
- }
狹義的理解,覆蓋僅指子類覆蓋父類的具體方法,即非抽象方法,在父類中提供了方法的默認實現方式,而子類採用不同的實現方式。
例如,以下代碼編譯會有誤。
- public abstract class Base{
- abstract void method1();
- abstract void method2();
- }
- public abstract class Sub extends Base{
- private void method(){//編譯有誤,不能縮小訪問權限
- System.out.println("method test!");
- }
- private abstract void method2();//編譯有誤,不能縮小訪問權限,且會出現以下錯誤
- }
- illegal combination of modifiers : abstract and private
- abstract 不能和 private 關鍵字一起使用。抽象方法必須需要被實現,但是 private 是私有的,外界無法訪問,所以不行。
- 10)父類的非抽象方法可以被覆蓋爲抽象方法
- public class Base{
- void methos();
- }
- public abstract class Sub extends Base{
- public abstract void method(); //合法
- }
Java 方法重載 (OverLoad)與方法覆蓋(Override)的區別
方法覆蓋和方法重載具有以下相同點
都要求方法名相同
都可以用於抽象方法和非抽象方法之間
不同點
方法覆蓋要求參數簽名必須一致,而方法重載要求參數簽名必須不一致
方法覆蓋返回類型必須一致,方法重載對此不做限制
方法覆蓋只能用於子類覆蓋父類的方法,方法重載用於同一個類的所有方法(包括從父類中繼承而來的方法)
方法覆蓋對方法的訪問權限和拋出的異常有特殊的要求,而方法重載在這方面沒有任何限制
父類的一個方法只能被子類覆蓋一次,而一個方法在所在類中可以被重載多次