第八天(下) 多態
Java作為面向對象的語言,也可以描述一個事物的兩種狀態,student繼承person 一個student對象即是student也是person
Java中多態的代碼體現在一個子類對象(實現類對象)既可以給這個子類(實現類對象)引用變量賦值,又可以給這個子類(實現類對象)的父類(接口)變量賦值。
如Student類可以為Person類的子類。那麽一個Student對象既可以賦值給一個Student類型的引用,也可以賦值給一個Person類型的引用。
最終多態體現為父類引用變量可以指向子類對象。
什麽是多態:
多態性是允許你將父對象設置成為一個或更多的他的子對象相等的技術,賦值之後,父對象就可以根據當前賦值給它的子對象的特性以不同的方式運作.
多態分類(這裏好像有分歧)
多態一般分為兩種:重寫式多態和重載式多態。
重載式多態,也叫編譯時多態。也就是說這種多態再編譯時已經確定好了。重載大家都知道,方法名相同而參數列表不同的一組方法就是重載。在調用這種重載的方法時,通過傳入不同的參數最後得到不同的結果。
重寫式多態,也叫運行時多態。這種多態通過動態綁定(dynamic binding)技術來實現,是指在執行期間判斷所引用對象的實際類型,根據其實際的類型調用其相應的方法。也就是說,只有程序運行起來,你才知道調用的是哪個子類的方法。 這種多態通過函數的重寫以及向上轉型來實現,我們上面代碼中的例子就是一個完整的重寫式多態。我們接下來講的所有多態都是重寫式多態,因為它才是面向對象編程中真正的多態。
多態的好處
可替換性多態對已存在代碼具有可替換性。
可擴充性多態對代碼具有可擴充性。增加新的子類不影響已存在類的多態性、繼承性,以及其他特性的運行和操作。
接口性
靈活性它在應用中體現了靈活多樣的操作,提高了使用效率
簡化性多態簡化對應用軟件的代碼編寫和修改過程,尤其在處理大量對象的運算和操作時,這個特點尤為突出和重要。
多態實現的必要條件:
繼承,重寫,向上轉型.(父類引用指向子類對象)
向上轉型
當子類對象賦值給一個父類引用時,便是向上轉型,多態本身就是向上轉型的過程
使用格式:
父類類型 變量名 = new 子類類型();
如:Person p = new Student();
向上轉型存在一些缺陷,那就是他必定會導致引用只能訪問父類的變量和方法以及子類重寫的方法
向下轉型
一個已經向上轉型的對象可以使用強制類型轉換的格式,將父類引用轉換為子類引用,這個過程就是向下轉型.
使用格式:
子類類型 變量名 = (子類類型) 父類類型的變量;
如:Student stu = (Student) p; //變量p 實際上指向Student對象
java語言的多態機制導致了引用變量的聲明類型和其實際引用對象的類型可能不一致,再結合虛方法調用規則可以得出結論:聲明為同種類型的兩個引用變量調用同一個方法時也可能會有不同的行為。這裏就引入了instanceof運算符。
舉個例子總不能把狗強制轉換成人把
instanceof運算符
java 中的instanceof 是一個二元操作符(運算符)運算符,由於是字母組成,所以是Java的保留關鍵字,但是和>=,<=,==屬同一類,它的作用是用來判斷,instanceof 左邊對象是否為instanceof 右邊類的實例,返回一個boolean類型值。還可以用來判斷子父類的所屬關系。
用法boolean r = object instanceof class
如果 object 是 class 的一個實例,則 instanceof 運算符返回 true。如果 object 不是指定類的一個實例,或者 object 是 null,則返回 false。
if(p instanceof student) { student s=(student)p;}
什麽時候用向上轉型
當不需要面對子類類型時,通過提高擴展性,或者使用父類的功能就能完成相應的操作,這時候就可以使用向上轉型
什麽時候使用向下轉型
要使用子類特有的功能時
轉型時要先用instanceof進行判斷
多態中的成員的特點
成員變量
編譯時 參考父類中又沒有這個變量,如果有編譯成功
運行時 運行的是父類中的變量值
成員方法
編譯時 參考父類中又沒有這個方法,如果有就編譯成功
運行時 運行時運行是子類的重寫方法
多態的總結
指向子類的父類引用由於向上轉型了,它只能訪問父類中擁有的方法和屬性,而對於子類中存在而父類中不存在的方法,該引用是不能使用的。若子類重寫了父類中的某些方法,在調用該些方法的時候,必定是使用子類中定義的這些方法(動態綁定)
實現多態的核心 動態綁定
動態綁定
動態綁定是指在執行期間半段所引用對象的實際類型,根據實際類型調用其相應的方法
多態調用的三種格式
* A:多態的定義格式:
* 就是父類的引用變量指向子類對象
父類類型 變量名 = new 子類類型();
變量名.方法名();
* B: 普通類多態定義的格式
父類 變量名 = new 子類();
舉例:
class Fu {} class Zi extends Fu {} //類的多態使用 Fu f = new Zi();
* C: 抽象類多態定義格式
抽象類 變量名 = new 抽象類子類();
舉例:
abstract class Fu { public abstract void method(); } class Zi extends Fu { public void method(){ System.out.println(“重寫父類抽象方法”); } } //類的多態使用 Fu fu= new Zi();
* D: 接口多態定義的格式
//接口 變量名 = new 接口實現類(); interface Fu { public abstract void method(); } class Zi implements Fu { public void method(){ System.out.println(“重寫接口抽象方法”); } } //接口的多態使用 Fu fu = new Zi();
* E: 註意事項
同一個父類的方法會被不同的子類重寫。在調用方法時,調用的為各個子類重寫後的方法。
如 Person p1 = new Student();
Person p2 = new Teacher();
p1.work(); //p1會調用Student類中重寫的work方法
p2.work(); //p2會調用Teacher類中重寫的work方法
當變量名指向不同的子類對象時,由於每個子類重寫父類方法的內容不同,所以會調用不同的方法。
多態舉例
* A: 畢老師和畢姥爺的故事
* 案例:
/*
描述畢老師和畢姥爺,
畢老師擁有講課和看電影功能
畢姥爺擁有講課和釣魚功能
*/
class 畢姥爺 {
void 講課() {
System.out.println("政治");
}
void 釣魚() {
System.out.println("釣魚");
}
}
// 畢老師繼承了畢姥爺,就有擁有了畢姥爺的講課和釣魚的功能,
// 但畢老師和畢姥爺的講課內容不一樣,因此畢老師要覆蓋畢姥爺的講課功能
class 畢老師 extends 畢姥爺 {
void 講課() {
System.out.println("Java");
}
void 看電影() {
System.out.println("看電影");
}
}
public class Test {
public static void main(String[] args) {
// 多態形式
畢姥爺 a = new 畢老師(); // 向上轉型
a.講課(); // 這裏表象是畢姥爺,其實真正講課的仍然是畢老師,因此調用的也是畢老師的講課功能
a.釣魚(); // 這裏表象是畢姥爺,但對象其實是畢老師,而畢老師繼承了畢姥爺,即畢老師也具有釣魚功能
// 當要調用畢老師特有的看電影功能時,就必須進行類型轉換
畢老師 b = (畢老師) a; // 向下轉型
b.看電影();
}
第八天(下) 多態