Java基礎——繼承、多型
1. 面向物件的特徵
我們都知道,java是面向物件的語言,那麼面向物件的有哪些特徵呢?
(1)抽象:將客觀事物的共性抽象出來,並將這些屬性歸為一個類。
包括兩個方面:過程抽象;資料抽象
(2)繼承:一個新類可以從現有的類中派生。
(3)封裝:將客觀事物抽象成類,每個類對自身的資料和方法實現保護。
(4)多型:主要有兩種表現方式:方法的過載;方法的覆蓋。
抽象和封裝比較簡單,我們重點講解繼承和多型。
2.什麼是繼承?
通過繼承,子類可以使用父類中的一些成員變數與方法,提供程式碼的複用性。
使用格式:class 子類名 extends 父類名
特性:(1)不支援多重繼承,子類至多隻能有一個父類,但可以通過實現多個介面來達到多重繼承的目的(介面支援多重繼承)
(2)子類只能繼承父類的非私有(public 和 protected)成員變數和方法。
(3)當子類中定義的成員變數和父類定義的成員變數同名時,子類的成員變數會覆蓋父類的成員變數,而不會繼承。
(4)當子類中的方法與父類中的方法有相同的函式簽名(相同方法名,相同引數個數和型別)時,子類將會覆蓋父類的方法,不會繼承。
3. 什麼是多型?
它表示當同一個操作作用在不同物件時,會有不同的語義,從而產生不同的結果。
(1)方法的過載(overload)
(2)方法的覆蓋 (override):子類可以覆蓋父類的方法,因此同樣的方法會在父類與子類中有著不同的表現形式。因為父類引用不僅能指向父類物件,也能指向子類物件。所以,在執行期間(非編譯期間)需要判斷引用物件的實際型別,根據實際型別判斷並呼叫相應的屬性和方法。因此,也方法的覆蓋 (override)被稱為執行時多型。
執行時多型存在的三個必要條件
一、要有繼承;
二、要有重寫;
三、父類引用指向子類物件。
class Base{ //父類的構造方法 public Base() { g(); } public void f() { System.out.println("Base f()"); } public void g() { System.out.println("Base g()"); } } class Derived extends Base{ //覆蓋了父類的f()方法 public void f() { System.out.println("Derived f()"); } //覆蓋了父類的g()方法 public void g() { System.out.println("Derived g()"); } } public class Test{ public static void main(String[] args) { Base b = new Derived(); //父類的引用指向了子類的例項化物件 b.f(); b.g(); } }
父類提供了無引數的建構函式,因此編譯器預設呼叫父類的無引數建構函式。在子類中覆蓋了父類的 f()方法和 g()方法,因此執行時例項化物件呼叫子類中相應的方法.
執行結果:
Derived g()
Derived f()
Derived g()
只有類中的方法才有多型的概念,類中的成員變數沒有多型的概念。
成員變數的值並不取決於建立物件的型別,而是取決於所定義變數的型別。
class Base{
public int i =1;
}
class Derived extends Base{
public int i = 2;
}
public class Test {
public static void main(String[] args) {
Base b = new Derived(); //變數型別為Base
System.out.println(b.i);
}
}
輸出結果:1
i 的值並不取決於建立物件的型別,而取決於定義變數的型別。變數b的型別為Base ,所以輸出為1,這是在編譯時就能確定的,也不具備多型性。
Base b = new Derived(); 改為Derived b = new Derived();則輸出為2
總結:
過載的注意點:
(1)過載是通過不同的方法引數來區分的,例如不同的引數個數、不同的引數型別、不同的引數順序。不能通過方法的訪問許可權、返回值型別和丟擲的異常型別來進行過載。
(2)對於繼承來說,如果父類方法的訪問許可權為private,那麼不能在子類中對其過載。如果在子類中也定義了一個同名的函式,這只是一個新的方法,不會達到過載的效果。(繼承只能繼承父類的非私有成員)
覆蓋的注意點:
(1)子類中的覆蓋方法必須和父類中被覆蓋方法有相同的函式名、引數、返回值。
(2)子類的覆蓋方法所丟擲的異常必須和父類被覆蓋的方法所丟擲的異常一致。
(3)父類中被覆蓋的方法不能為private,否則其子類只是定義了一個方法,並沒有對其覆蓋。
4.過載和覆蓋的區別
(1)覆蓋是子類和父類之間的關係,是垂直的關係;過載是同一個類中方法之間的關係,是水平關係。
(2)覆蓋要求引數列表相同;過載要求引數列表不同
(3)覆蓋關係中,呼叫方法體是根據物件的型別來決定的,而過載關係是根據呼叫時的實引數表與形引數表來選擇方法體的。
如下程式碼的執行結果是什麼?
答案:編譯報錯。因為函式是不能以返回值來區分的,雖然父類與子類中的函式有著不同的返回值,但它們有著相同的方法簽名,因此,編譯器無法區分。
class Super{
public int f()
{
return 1;
}
}
public class SubClass extends Super{
public float f() //這裡會報錯
{
return 2f;
}
public static void main(String[] args) {
Super s = new SubClass();
System.out.println(s.f());
}
}