繼承、重寫和多態
還是最經典的例子,Animal, Cat, Dog
繼承
關於繼承,先總結,再給出代碼示例
關於Java中的繼承:
- 1 繼承繼承是面向對象三大特徵之一 (封裝、 繼承、 多態)
- 2 繼承的基本作用:代碼複用,但是繼承最重要的作用是有了繼承纔有了 “重寫” 和 “多態機制”。
- 3 繼承語法格式:
class 類名 extends 父類名 { 屬性 ; 方法() }
- 4 java中只支持單繼承,一個類不能同時繼承多個類。
- 5 一些基本術語:
/** * A可以稱爲:父類、基類、超類、superclass * B可以稱爲:子類、派生類、subclass **/ class A extends B {}
- 6 私有的不繼承
構造方法不繼承
其他數據均可繼承- 7 雖然java不支持多繼承,但可以間接繼承,例如:
/** * A直接繼承B, A間接繼承C, D **/ class A extends B {} class B extends C {} class C extends D {}
- 8 所有類默認繼承java.lang.Object類
示例
public class Animal {
public void move() {
System.out.println("Animal is moving");
}
}
public class Cat extends Animal {
@override // 重寫
public void move() {
System.out.println("cat is moving");
}
}
public class Test {
public static void main(String[] args) {
Cat cat = new Cat();
cat.move();
}
}
運行結果:
cat is moving(若Cat類中沒有重寫,則結果爲 Animal is moving)
重寫
- 當父類方法無法滿足子類的業務需求時,子類有必要將父類中繼承的方法進行重寫。(例如上面的Animal和Cat)
- 重寫需要滿足的條件:
方法重寫發生在具有繼承關係的父子類之間
返回值類型相同,方法名相同,形參列表相同
訪問權限不能更低,拋出異常不能更多 - 私有方法不能繼承,所以更不能重寫
- 構造方法不能繼承,更不能重寫
- 靜態方法不存在重寫
- 重寫只針對方法,不談屬性
多態
public class Animal {
public void move() {
System.out.println("animal is moving");
}
}
public class Cat extends Animal {
// 重寫父類方法
@Override
public void move() {
System.out.println("cat is moving");
}
// 不是從父類繼承的,子類特有方法
public void catchMouse() {
System.out.println("catch mouse");
}
}
public class Dog extends Animal {
@Override
public void move() {
System.out.println("dog is moving");
}
}
/**
* 關於多態:
* 1 Animal,Cat,Dog之間的關係:
* Cat和Dog都繼承自Animal
* Cat和Dog之間沒有繼承關係
* 2 關於多態的幾個概念:
* 向上轉型(自動類型轉換): 子 --> 父
* 向下轉型(強制類型轉換): 父 --> 子
* 轉型時,兩者之間必須要有繼承關係
* 沒有繼承關係,無法編譯通過
*/
public class Test {
public static void main(String[] args) {
Animal a = new Cat();
/**
* 1 編譯階段編譯器檢查"a"這個引用的數據類型爲Animal,由於Animal.class字節碼中有move()方法,
* 所以編譯通過。這個過程稱爲靜態綁定,編譯階段綁定,只有靜態綁定成功之後纔有後續的運行。
*
* 2 程序運行階段,JVM堆內存當中真實創建的對象是Cat對象,則程序在運行時會調用Cat的move()方法,
* 此時發生了程序的動態綁定,運行階段綁定。
*
* 3 注意:這裏 a 調用的move()方法是 Cat 的move()方法,從父類繼承而來,與是否重寫無關
*/
a.move();
/**
* a.catchMouse()無法通過編譯,原因:
* 1 Java程序分爲編譯階段和運行階段
* 2 先分析編譯階段,再分析運行階段。編譯無法通過,程序無法運行
* 3 編譯階段編譯器檢查"a"這個引用的數據類型爲Animal,不存在catchMouse()方法,
* 導致靜態綁定失敗,編譯失敗,更別談運行了。
*/
// a.catchMose();
}
}
運行結果:
cat is moving
那麼該如何調用catchMose()方法呢?
強制類型轉換
- 爲了使用子類特有的方法,我們需要進行強制類型轉換,同樣是上面的Animal和Cat
public class Test { public static void main(String[] args) { Animal a2 = new Cat(); Cat c = (Cat) a2; c.catchMouse; } }
運行結果:
catch mouse
- 補充一個非常經典的異常 :
java.lang.ClassCastException
public class Test { public static void main(String[] args) { Aniaml a3 = new Dog(); Cat c1 = (Cat) a3; } }
- 以上程序在編譯時沒有錯誤,因爲編譯階段 a3 是一個Animal類型,可以強轉爲Cat
- 但是運行時會報錯,因爲真實對象是Dog類型,與Cat之間不存在繼承關係
- 運行結果:
- 以上異常只會出現在向下轉型時,向上轉型只要編譯通過就不會有問題
當然啦,這個異常我們也是有辦法應對的,那就是 instanceof
- instanceof返回的是一個boolean類型的值
public class Test { public static void main(String[] args) { Animal a3 = new Dog(); if (a3 instanceof Cat) { // a3是否爲Cat的實例 Cat c = (Cat) a3; c.catchMouse(); } else if (a3 instanceof Dog) { Dog d = (Dog) a3; d.move(); } } }
運行結果:
dog is moving