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規範。