java 重載、重寫、構造函數詳解

方法重寫java

一、重寫 只能出如今繼承關係之中。當一個類繼承它的父類方法時,都有機會重寫該父類的方法。一個 特例是父類的方法被標識爲final。重寫的主要優勢是可以定義某個子類型特有的行爲。
 class Animal {
       public void eat(){
           System.out.println ("Animal is eating.");
       }
   }
  
   class Horse extends Animal{
       public void eat(){
           System.out.println ("Horse is eating.");
       }
   }

二、對於從父類繼承來的抽象方法,要麼在子類用重寫的方式設計該方法,要麼把子類也標識爲抽象的。因此抽象方法能夠說是必需要被重寫的方法。

三、重寫的意義。
重寫方法能夠實現多態,用父類的引用來操縱子類對象,可是在實際運行中對象將運行其本身特有的方法。
   public class Test {
       public static void main (String[] args) {
           Animal h = new Horse();
           h.eat();  
       }
   }

   class Animal {
       public void eat(){
           System.out.println ("Animal is eating.");
       }
   }
  
   class Horse extends Animal{
       public void eat(){
           System.out.println ("Horse is eating.");
       }
       public void buck(){
       }
   }
  一個原則是:使用了什麼引用,編譯器就會只調用引用類所擁有的方法。若是調用子類特有的方法,如上例的h.buck(); 編譯器會抱怨的(編譯錯誤)。也就是說,編譯器只看引用類型,而不是對象類型。

四、重寫方法的 規則
  若想實現一個合格重寫方法,而不是重載,那麼必須同時知足下面的要求!

A、重寫規則之一: 重寫方法不能比被重寫方法限制有更嚴格的訪問級別
(可是能夠更普遍,好比父類方法是包訪問權限,子類的重寫方法是public訪問權限。)
  好比:Object類有個toString()方法,開始重寫這個方法的時候咱們總容易忘記public修飾符,編譯器固然不會放過任何教訓咱們的機會。出錯的緣由就是:沒有加任何訪問修飾符的方法具備包訪問權限,包訪問權限比public固然要嚴格了,因此編譯器會報錯的。

B、重寫規則之二: 參數列表必須與被重寫方法的相同
  重寫有個孿生的弟弟叫重載,也就是後面要出場的。若是子類方法的參數與父類對應的方法不一樣,那麼就是你認錯人了,那是重載,不是重寫。

C、重寫規則之三: 返回類型必須與被重寫方法的返回類型相同
  父類方法A:void eat(){} 子類方法B:int eat(){} 二者雖然參數相同,但是返回類型不一樣,因此 不是重寫
  父類方法A:int eat(){}   子類方法B:long eat(){} 返回類型雖然兼容父類,可是不一樣就是不一樣,因此 不是重寫

D、重寫規則之四: 重寫方法不能拋出新的異常或者比被重寫方法聲明的檢查異常更廣的檢查異常。可是能夠拋出更少,更有限或者不拋出異常。
   import java.io.*;
   public class Test {
       public static void main (String[] args) {
           Animal h = new Horse();
           try {
               h.eat();  
           }
           catch (Exception e) {
           }
       }
   }

   class Animal {
       public void eat() throws Exception{
           System.out.println ("Animal is eating.");
           throw new Exception();
       }
   }
  
   class Horse extends Animal{
       public void eat() throws IOException{
           System.out.println ("Horse is eating.");
           throw new IOException();
       }
   }
  這個例子中,父類拋出了檢查異常Exception,子類拋出的IOException是Exception的子類,也便是比被重寫的方法拋出了更有限的異常,這是能夠的。若是反過來,父類拋出IOException,子類拋出更爲寬泛的Exception,那麼不會經過編譯的。
注意:這種限制只是針對檢查異常,至於運行時異常RuntimeException及其子類再也不這個限制之中。

E、重寫規則之五: 不能重寫被標識爲final的方法。

F、重寫規則之六: 若是一個方法不能被繼承,則不能重寫它。
  比較典型的就是父類的private方法。下例會產生一個有趣的現象。
   public class Test {
       public static void main (String[] args) {
           //Animal h = new Horse();
           Horse h = new Horse();
           h.eat();
       }
   }

   class Animal {
       private void eat(){
           System.out.println ("Animal is eating.");
       }
   }
  
   class Horse extends Animal{
       public void eat(){
           System.out.println ("Horse is eating.");
       }
   }
  這段代碼是能經過編譯的。表面上看來違反了第六條規則,但實際上那是一點巧合。Animal類的eat()方法不能被繼承,所以 Horse類中的 eat()方法是一個全新的方法,不是重寫也不是重載,只是一個只屬於Horse類的全新的方法!這點讓不少人迷惑了,可是也不是那麼難以理解。
  main()方法若是是這樣:
   Animal h = new Horse();
   //Horse h = new Horse();
   h.eat();
  編譯器會報錯,爲何呢?Horse類的eat()方法是public的啊!應該能夠調用啊!請牢記,多態只看父類引用的方法,而不看子類對象的方法!

方法的重載編程

  重載是友好的,它不要求你在調用一個方法以前轉換數據類型,它會自動地尋找匹配的方法。方法的重載是在編譯時刻就決定調用哪一個方法了,和重寫不一樣。最最經常使用的地方就是構造器的重載。

一、基本數據類型參數的重載。app

   public class Test {
       static void method(byte b){
           System.out.println ("method:byte");
       }
       static void method(short s){
           System.out.println ("method:short");
       }
       static void method(int i){
           System.out.println ("method:int");
       }
       static void method(float f){
           System.out.println ("method:float");
       }
       static void method(double d){
           System.out.println ("method:double");
       }
       public static void main (String[] args) {
           method((byte)1);
           method('c');
           method(1);
           method(1L);
           method(1.1);
           method(1.1f);
       }
   }

輸出結果:函數

method:byte
method:int
method:int
method:float
method:double
method:float

  能夠看出:首先要尋找的是數據類型正好匹配方法。若是找不到,那麼就提高爲表達能力更強的數據類型,如上例沒有正好容納long的整數類型,那麼就轉換爲 float類型的。若是經過提高也不能找到合適的兼容類型,那麼編譯器就會報錯。反正是不會自動轉換爲較小的數據類型的,必須本身強制轉換,本身來承擔轉變後果。

  char類型比較特殊,若是找不到正好匹配的類型,它會轉化爲int而不是short,雖然char是16位的。

二、重載方法的規則。

A、被重載的方法必須改變參數列表
參數必須不一樣,這是最重要的!不一樣有兩個方面,參數的個數,參數的類型,參數的順序。

B、被重載的方法與返回類型無關。
也就是說,不能經過返回類型來區分重載方法。

C、被重載的方法能夠改變訪問修飾符。
沒有重寫方法那樣嚴格的限制。

D、被重載的方法能夠聲明新的或者更廣的檢查異常。
沒有重寫方法那樣嚴格的限制。

E、方法可以在一個類中或者在一個子類中被重載。


三、帶對象引用參數的方法重載。this

   class Animal {}
   class Horse extends Animal{}
  
   public class Test {
       static void method(Animal a){
           System.out.println ("Animal is called.");
       }
       static void method(Horse h){
           System.out.println ("Horse is called.");
       }
       public static void main (String[] args) {
           Animal a = new Animal();
           Horse h = new Horse();
           Animal ah = new Horse();
          
           method(a);
           method(h);
           method(ah);
        }
   }

輸出結果是:編碼

Animal is called.
Horse is called.
Animal is called.

  前兩個輸出沒有任何問題。第三個方法爲何不是輸出「Horse is called.」呢?仍是那句老話,要看引用類型而不是對象類型,方法重載是在編譯時刻就決定的了,引用類型決定了調用哪一個版本的重載方法。


四、重載和重寫方法區別的小結。
  若是能完全弄明白下面的例子,說明你對重載和重寫很是瞭解了,能夠結束這節的複習了。spa

   class Animal {
       public void eat(){
           System.out.println ("Animal is eating.");  
       }
   }
   class Horse extends Animal{
       public void eat(){
           System.out.println ("Horse is eating.");  
       }
       public void eat(String food){
           System.out.println ("Horse is eating " + food);
       }
   }
  
   public class Test {
       public static void main (String[] args) {
           Animal a = new Animal();
           Horse h = new Horse();
           Animal ah = new Horse();
          
           a.eat();
           h.eat();
           h.eat("apple");
           ah.eat();
           //a.eat("apple");
           //ah.eat("apple");
        }
   }

四個輸出分別是什麼?被註釋的兩條語句爲何不能經過編譯?
第一條:a.eat(); 普通的方法調用,沒有多態,沒什麼技術含量。調用了Animal類的eat()方法,輸出:Animal is eating.
第二條:h.eat(); 普通的方法調用,也沒什麼技術含量。調用了Horse類的eat()方法,輸出:Horse is eating.
第三條:h.eat("apple"); 重載。Horse類的兩個eat()方法重載。調用了Horse類的eat(String food)方法,輸出:Horse is eating apple
第四條:ah.eat(); 多態。前面有例子了,不難理解。輸出:Horse is eating.
第五條:a.eat("apple"); 低級的錯誤,Animal類中沒有eat(String food)方法。所以不能經過編譯
第六條:ah.eat("apple"); 關鍵點就在這裏。解決的方法仍是那句老話,不能看對象類型,要看引用類型。Animal類中沒有eat(String food)方法。所以不能經過編譯

小結一下:多態不決定調用哪一個重載版本;多態只有在決定哪一個重寫版本時才起做用。
重載對應編譯時,重寫對應運行時。夠簡潔的了吧!

設計

構造方法code

  構造方法是一種特殊的方法,沒有構造方法就不能建立一個新對象。實際上,不只要調用對象實際類型的構造方法,還要調用其父類的構造方法,向上追溯,直到 Object類。構造方法沒必要顯式地調用,當使用new關鍵字時,相應的構造方法會自動被調用。

一、構造方法的規則
A、構造方法能使用任何訪問修飾符。包括private,事實上java類庫有不少都是這樣的,設計者不但願使用者建立該類的對象。

B、構造方法的名稱必須與類名相同。這樣使得構造方法不同凡響,若是咱們遵照sun的編碼規範,彷佛只有構造方法的首字母是大寫的。

C、構造方法不能有返回類型
反過來講,有返回類型的不是構造方法對象

   public class Test {
       int Test(){
           return 1;
       }
   }

  這個方法是什麼東西?一個冒充李逵的李鬼而已,int Test()和其餘任何普通方法沒什麼兩樣,就是普通的方法!只不過看起來很噁心,相似噁心的東西在考試卷子裏比較多。

D、如果不在類中建立本身的構造方法,編譯器會自動生成默認的不帶參數的構造函數
  這點很容易驗證!寫一個這樣簡單的類,編譯。

class Test {
}

對生成的Test.class文件反編譯:javap Test,能夠看到:

D:"JavaCode"bin>javap Test
Compiled from "Test.java"
class Test extends java.lang.Object{
    Test();
}

  看到編譯器自動添加的默認構造函數了吧!

E、若是隻建立了帶參數的構造方法,那麼編譯器不會自動添加無參的構造方法的

F、在每一個構造方法中,若是使用了重載構造函數this()方法,或者父類的構造方法super()方法,那麼this()方法或者super()方法必須放在第一行。並且這兩個方法只能選擇一個,所以它們之間沒有順序問題。

G、除了編譯器生成的構造方法,並且沒有顯式地調用super()方法,那麼編譯器會插入一個super()無參調用。

H、抽象類有構造方法。

靜態方法的重載與重寫(覆蓋)

一、靜態方法是不能被覆蓋的。能夠分兩種狀況討論:

A、子類的非靜態方法「覆蓋」父類的靜態方法。
  這種狀況下,是不能經過編譯的。

class Father{
     static void print(){
         System.out.println ( " in father   method " );
     }
}
class Child extends Father{
     void print(){
         System.out.println ( " in child method " );
     }
}

  static方法表示該方法不關聯具體的類的對象,能夠經過類名直接調用,也就是編譯的前期就綁定了,不存在後期動態綁定,也就是不能實現多態。子類的非靜態方法是與具體的對象綁定的,二者有着不一樣的含義。

B、子類的靜態方法「覆蓋」父類靜態方法。
  這個覆蓋依然是帶引號的。事實上把上面那個例子Child類的print方法前面加上static修飾符,確實能經過編譯!可是不要覺得這就是多態!多態的特色是動態綁定,看下面的例子:

class Father{
     static void print(){
         System.out.println ( " in father   method " );
     }
}
class Child extends Father{
     static void print(){
         System.out.println ( " in child method " );
     }
}

class Test{
     public static void main (String[] args) {
         Father f = new Child();
         f.print();
     }
}

  輸出結果是:in father method
  從這個結果能夠看出,並無實現多態。
  可是這種形式很迷惑人,貌似多態,實際編程中千萬不要這樣搞,會把你們搞懵的!
  它不符合覆蓋表現出來的特性,不該該算是覆蓋!
  總而言之,靜態方法不能被覆蓋

二、靜態方法能夠和非靜態方法同樣被重載。
這樣的例子太多了,我不想寫例程了。看看java類庫中不少這樣的例子。
如java.util.Arrays類的一堆重載的binarySearch方法。
在這裏提一下是由於查資料時看到這樣的話「sun的SL275課程說,靜態方法只能控制靜態變量(他們自己沒有),靜態方法不能被重載和覆蓋……」
你們不要相信啊!能夠重載的。並且靜態與非靜態方法能夠重載。

從重載的機制很容易就理解了,重載是在編譯時刻就決定的了,非靜態方法均可以,靜態方法怎麼可能不會呢? 

 

  致謝:感謝您的耐心閱讀!

相關文章
相關標籤/搜索