java基礎--覆蓋與重載

導語
昨天看spring源碼時發現重載和覆蓋用的很多,又想起之前面試實習居然混淆了,平時不愛看書,又不總結,對於基礎知識感覺懂又不全懂。

直接看代碼

一個簡單的例子:

public interface Animal {
    default Animal getInstance() {return this;}
    default void perform(){System.out.println("default perform...");}
}
public class Dog implements Animal {   
    @Override
    public void perform() {System.out.println("dog is in the performance...");}
}
public class Cat implements Animal {
    @Override
    public Cat getInstance() {return this;}
}
public class Main {
    public static void main(String[] args) {
        Animal animal = new Dog();
        animal.perform();
    }
}

這個例子很簡單,但還是要拿出來,主要是Cat複寫getInstance時,改變了其返回值,依舊是覆蓋。但是如果改成不是原返回值類型的子類時則會報錯,不是重載也不是繼承,直接是函數重名錯誤。那麼就可以推廣到,一個類無法實現了有方法重名且返回值沒有繼承關係的兩個接口。

複雜點的例子:

public abstract class AbstractAnimal implements Animal{
    @Override
    """
    記住,我們是商人.
    """
    public abstract Animal getInstance() throws Exception;
    @Override
    public final void perform (){
        try {
            this.getInstance().perform();
        } catch (Exception e) {
            System.out.println("Animals are missing...");
        }
    }
    protect abstract buyAnimals();
}

public class Zookeeper extends AbstractAnimal {

    public static int times = 0;
    List<Animal> animals;
    public Zookeeper() {
        this.buyAnimals();
    }
    @Override
    public Animal getInstance() throws Exception {
        if(animals== null || animals.size()==0)
            throw new Exception("Zookeeper has no animals...");
        times++;
        return animals.get(times % animals.size());
    }
    public final void perform(int index) {
        if(animals.size() <= index)
            super.perform();
        else
            this.animals.get(index).perform();
    }
    public void perform(Class type) {
        for(Animal a: animals) {
            if(a.getClass() == type) {
                a.perform();
                return;
            }
        }
        System.out.println("no such animals: " + type.getClass().getSimpleName()) ;
    }
    /**
    public Animal perform(int index) {
        if(animals.size() <= index)
            index = animals.size();
        Animal animal = animals.get(index);
        animal.perform();
        return animal;
     }
     */
    @Override
    public void buyAnimals() {
        if(animals == null)
            animals = new ArrayList<>();
        this.animals.add(new Cat());
        this.animals.add(new Dog());
        this.animals.add(new Cat());
    }
}
public class Main {
    public static void main(String[] args) {
        Animal zookeeper = new Zookeeper();
        zookeeper.perform();
        //  zookeeper.perform(3);  無法使用
        zookeeper.perform();
        Zookeeper zookeeper1 = new Zookeeper();
        zookeeper1.perform();
        zookeeper1.perform();
        zookeeper1.perform(2) 
    }
}

Zookeeper繼承自AbstractAnimal,而它的父親AbstractAnimal很聰明,它把getInstance()覆蓋爲抽象方法,出於害怕把buyAnimals()權限設置成protect,並且覆蓋Animal中的perform方法並規定perform無法再被它子類覆蓋。繼承後的Zookeeper知道父親的用意,但公開地購買很多的Animal,實現了父親的getInstance,讓買來的Animal輪流替他perform,並且又重載出很多花樣的perform。

重載和覆蓋做了什麼?

個人覺得:
覆蓋做的是去掉層層繼承與實現中會被java虛擬機認爲是同一個函數的。
重載做的是如何讓虛擬機認識這兩個函數雖然名稱相同,但實質不同。
我們看一行很常用的代碼:a.f(argv), 其中a爲某個實例對象,f爲方法名,argv爲若干個參數或者空
假如我們重寫父類中的方法不會被覆蓋掉,按照繼承可以獲得父類所有可訪問的方法,那麼子類中將會有兩個方法假設名爲f,且參數列表相同,那麼調用f時該如何抉擇呢?顯然只給的a,f,argv三個參數虛擬機無法去抉擇,必須覆蓋掉一個。但是我們確實有寫幾個名稱相同的方法的需求,此時a和f都是相同的,那麼只有通過argv來區分了,這個過程大概就是重載吧。
先看函數名相同各種的情況:

  1.參數列表(參數列表指參數類型順序,個數)
  2.final修飾
  3.權限修飾符
  4.返回值類型
  5.abstract修飾

函數重載時可以發生在本類中和本類與父類中,本類與父接口的默認方法中,參數列表必定不同,和final修飾,權限修飾,返回值類型,abstract修飾無關係。
方法覆蓋只能發生在子類與父類或子類與父接口的默認方法間,函數名相同,參數列表相同,返回值類型可以不同但必須是其原類型的子類,不能覆蓋final修飾的方法,abstract修飾符符合java規範即可。

幾種特別注意的函數名相同:
1.子類覆蓋父類方法do()時,提高了該方法的權限:

      Father f = new Son();
      f.do()    // 因爲Son中權限的提高導致do()無法調用

2.參數列表相同,但返回值不是父類中定義類型或其子類

     Father f = new Son()  方法重名。
     A a = f.getA()  //因爲被Son覆蓋爲返回B類型,這不是坑爹嗎?

3.參數列表相同,返回值是父類中定義類型的子類

    此時Father f = new Son()
    A a = f.getA() 形如A a = new B() 形如Father f = new Son() ,沒毛病.

4.抽象實現, 將父類方法改爲抽象實現

    此時子類必定是個抽象類,其抽象實現的父類方法將由子類的子類實現。沒毛病。

5.與父類方法參數列表不同,返回值相同。

    我給你三個參數,你卻去調用父類兩個參數的方法?顯然是重載。 

總結


一個對象實例中有名稱相同的兩個方法,若參數列表相同,且兩方法不在同一類中,要麼是覆蓋要麼是不符合java規範;若參數列表不同,要麼是重載,要麼是不符合java規範。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章