java:繼承和多型
一.繼承
1.繼承
①繼承是面向物件程式設計的三大特徵之一,是一種基於已有類來建立新類的機制。由繼承而得到的類稱為子類(或派生類),被繼承的類稱為父類(或超類)。
② Java中每個類只允許有一個父類。語法如下:class <子類> extends <父類>
(Object類是所有類的直接父類或間接父類。)
③根據訪問許可權修飾符的不同,子類可以繼承父類中某些成員變數和方法,提高了程式碼的重用性,子類也可以新增新的成員變數和方法
④如果類被final修飾,則該類不能被繼承(Java中已有的類(諸如Void、String、Class、Scanner、System、8種基本資料型別對應包裝類等類)已經被final修飾,所以這些類不能被繼承。)
2.父類對子類構造方法造成的影響
①如果父類擁有無參構造方法(無論隱式的還是顯式的)且子類中的構造方法又沒有明確指定呼叫父類的哪個構造方法,則子類中沒有呼叫該子類其它構造方法的構造方法使用super()隱式呼叫父類的無參構造方法super()放在第一行
②子類Student中該建構函式沒有使用this呼叫本類中其它建構函式,所有該函式中使用隱含的super()呼叫父類無參建構函式。
③子類Student中該建構函式使用this呼叫本類無參建構函式,則該建構函式中不存在super()呼叫父類無參建構函式的情況,這一點可以通過分析執行程式後的結果看出來
子類必須呼叫父類的構造方法
如果父類沒有無參構造方法(無論隱式的還是顯式的),則要求子類構造方法必須直接或間接指定呼叫父類哪個構造方法並且放在有效程式碼第一行
3.父類成員變數被覆蓋
當子類成員變數和父類成員變數同名時,對子類物件來講,父類的成員變數不能被子類繼承(即子類的成員變數覆蓋了父類的成員變數),此時稱子類的成員變數隱藏了父類的成員變數。
如果要在子類非static修飾的程式碼塊或方法中使用被隱藏的父類成員變數可以通過super關鍵字實現。
例子:
public class Student extends Father{ // String studentId; //子類可以繼承父類的屬性和方法也可以新增自己的屬性和方法 // public void pritn() { // // } //當子類沒有確定呼叫父類哪種構造方法時,預設呼叫父類無參構造方法 // super();//這一行寫不寫結果都一樣 呼叫父類無參構造方法 // System.out.println("Student無參構造方法"); // } // public static void main(String[] args) { // new Student(); // stu.id="xiao";//子類可以繼承父類的屬性和方法 // stu.studentId="5454545"; // } int age=10; public Student() { } public static void main(String[] args) { new Student(); } }
二.重寫
子類可以繼承父類方法,但有時從父類繼承的方法在子類中必須進行修改以適應新類的需要,這種對父類方法進行改寫或改造的現象稱為方法重寫或方法覆蓋。父類方法在子類中重寫使繼承更加靈活。
子類重寫了父類的方法,則使用子類建立的物件呼叫該方法時,呼叫的是重寫後的方法
①如果要在子類非static修飾的程式碼塊或方法中呼叫父類被重寫的方法可以通過super關鍵字實現
②子類重寫父類方法需滿足以下條件:
1.方法名和引數列表:子類重寫的方法和父類被重寫的方法在方法名和引數列表方面相同;
2.返回值型別:
如果父類被重寫的方法沒有返回值型別或者返回值型別為基本資料型別,則要求子類重寫的方法的返回值型別和父類被重寫方法的返回值型別相同;
如果父類被重寫的方法返回值型別為引用資料型別,則要求子類重寫的方法的返回值型別和父類被重寫方法的返回值型別相同或是其子類。
3.子類重寫的方法不能縮小父類被重寫方法的訪問許可權,子類重寫方法的訪問許可權必須大於等於父類被重寫方法的訪問許可權;
1.父類中靜態方法可以被子類繼承,但卻不能被子類重寫
2.重寫父類非靜態方法時,重寫後的方法不能新增static修飾
3.父類中被final關鍵字修飾的方法可以被子類繼承,但卻不能被子類重寫
final關鍵字
final關鍵字可以用來修飾類、方法和變數:
final修飾的類不能被繼承。
final修飾的方法不能被重寫。
final修飾的變數是常量,不允許二次賦值。
super使用原則:
super關鍵字可以呼叫父類的成員變數( super.屬性)和方法(super.父類方法([引數列表]))。
子類構造方法中可以使用super關鍵字呼叫父類的構造方法:super([引數列表]);
super 不能用於靜態方法或靜態程式碼塊中。
例子:
父類
public class Father {//public後如果加了final則該父類不能被子類繼承
// String name;//如果加了private則該屬性不能被子類繼承
// String id;
// public Father() {
// System.out.println("father無參構造方法");
// }
// int age;
// public Father() {
//
// }
// public Father(int age) {
// System.out.println(age);
// }
// public static void main(String[] args) {
// Father father= new Father();
//
// }
public Object eat() {//final加了以後子類就不能重寫
System.out.println("用筷子吃");
//加了static可以被子類使用但是不能重寫
return true;
}
}
子類
public class Son extends Father{
@Override//加了這個不出錯的就是重寫
public String eat() {
// 重新改寫父類的方法,以使子類的特徵能夠被準確的描述
System.out.println("用叉子吃飯");
//public不能換為private父類到子類只能擴大不能縮小
return"";
//重寫父類非靜態方法子類不能加static
/重寫的返回值型別和引數列表一定要相同,如果父類返回值型別為Object則子類可以是String
{
super.eat();//呼叫父類的方法
}
public static void main(String[] args) {
Son son =new Son();
son.eat();
}
}
三.多型
父類型別(比如Mammal)的變數(比如mammal1)指向子類建立的物件,使用該變數呼叫父類中一個被子類重寫的方法(比如move方法),則父類中的方法呈現出不同的行為特徵,這就是多型。
Java引用變數有兩種型別,分別是編譯時型別和執行時型別:編譯時型別由宣告該變數時使用的型別決定;執行時型別由實際賦給該變數的物件。如果編譯時型別和執行時型別不一致,就可能出現所謂多型。
上例分析:當把子類建立的物件直接賦給父類引用型別時,例如上例Test main方法中“Mammal mammal1 = new Whale();”, mammal1引用變數的編譯時型別是Mammal,執行時型別是Whale,當程式執行時,該引用變數mammal1呼叫父類中被子類重寫的方法時,其方法行為表現的是子類重寫該方法後的行為特徵,而不是父類方法的行為特徵。
1.上轉型物件
子類例項化的物件賦值給父類宣告變數,則該物件稱為上轉型物件,這個過程稱為物件上轉型,對應於資料型別轉換中的自動型別轉換
上轉物件呼叫父類方法,如果該方法已被子類重寫,則表現子類重寫後的行為特徵,否則表現父類的行為特徵。
使用上轉型物件呼叫成員變數,無論該成員變數是否已經被子類覆蓋,使用的都是父類中的成員變數
2.物件下轉型
可以將上轉型物件再強制轉換為建立該物件的子類型別的物件,即將上轉型物件還原為子類物件,對應於資料型別轉換中的強制型別轉換。
還原後的物件又具備了子類所有屬性和功能,即可以操作子類中繼承或新增的成員變數,可以呼叫子類中繼承或新增的方法。
注意:不可以將父類建立的物件通過強制型別轉換賦值給子類宣告的變數。
例子:
父類
package array;
/**
* 哺乳動物類
*/
public class Mammal {
int a=100;
public void move() {
System.out.println("哺乳動物可以移動");
}
}
子類
package array;
public class Whale extends Mammal{
int a=10;
public void eat() {
}
public void move() {
System.out.println("鯨魚靠鰭移動");
}
public static void main(String[] args) {
// Whale whale = new Whale();
// //whale.move();
// Mammal mammal=whale;//自動型別轉換 父類型別變數=子類建立的物件
// mammal.move();//多型 多種行為狀態
// System.out.println(mammal.a);
//mammal.a=10;出錯
//mammal.eat();出錯
Mammal mammal= new Whale();
Whale whale=(Whale)mammal;
System.out.println(whale.a);
}
}