JAVA學習第十五課(多態及其基本應用),java學習多態
多態:
物件導向的第三個特徵,定義:某類事物存在多種形態,比如,函數就具備多態性,同名的一個函數,它的參數列表不一樣,它的存在性就不一樣,還有同一個函數,放在父類和放在子類,它的存在性也就不一樣。
對象也存在多態性。
例子:動物有豬、貓、狗等
貓這個對象對應的類是貓類
貓 x = new 貓();
同時,貓還是動物的一種,也就可以把貓成為動物
動物 y = new 貓();
動物 z = new 狗();
動物是狗和貓集體事物中抽取出來的父類
父類引用指向了子類對象
一、概述
//對象的多態性//多態的表現,父類型在指向自對象class animal{}class cat extends animal{}class dog extends animal{}public class Main{public static void main(String[] args){//一個對象兩種形態animal 小動物 = new cat();//小動物通過貓建立對象,動物類在指向/* * 貓這類事物具備貓的形態,又具備動物的形態。 * 這就是事物的多態性 * 也就是一個對象對應著不同的類型 * 多態在代碼中的體現: * (父類/介面)的引用指向了其子類的對象 * */}}
二、多態的優點
提供了代碼的擴充性,前期定義的代碼可以使用後期的內容(先有了動物,才有了豬)
以下述代碼體現:
abstract class animal{abstract void sing();//叫}class cat extends animal{void sing(){System.out.println("喵喵叫");}void fun()//貓的特有功能{System.out.println("捉老鼠");}}class dog extends animal{void sing(){System.out.println("汪汪叫");}void fun()//狗的特有功能{System.out.println("看家");}}class pig extends animal{void sing(){System.out.println("哼哼叫");}void fun(){System.out.println("拱地");}}public class Main{public static void main(String[] args){//一隻貓cat 小貓 = new cat();小貓.sing();//很多貓cat 二貓 = new cat();cat 三貓 = new cat();catqun(二貓);catqun(三貓);//....//多態的體現,多隻動物dog 小狗1號 = new dog();cat 小貓1號 = new cat();catqun(小狗1號);catqun(小貓1號);catqun(new pig(););}static void catqun(animal c)//animal c = new cat()/dog()/pig();{c.sing();}}
三、多態的弊端和前提
1.多態的弊端:
前期定義的內容不能使用後期 子類的特有內容
static void catqun(animal c){c.sing();//c.fun();->animal裡沒有fun這個方法}
PS:當然可以直接使用貓static void catqun(cat c)來調用,但是我們不知道後期到底還會出現多少物種,複用性差
2.多態的前提:
⑴.必須有關係,要麼繼承,要麼實現
⑵.要有覆蓋(父類定義了功能,子類具體實現,狗叫、狼叫,很麻煩。犬科叫,很簡單)
如果不滿足⑵,沒有覆蓋就使用多態,比如:狗看家,正常,狼看家,這不就出問題了。
保證了多態的這兩個前提,就可以提高程式的擴充性
四、轉型
以代碼體現:
abstract class animal{abstract void sing();//叫}class cat extends animal{void sing(){System.out.println("喵喵叫");}void fun()//貓的特有功能{System.out.println("捉老鼠");}}class dog extends animal{void sing(){System.out.println("汪汪叫");}void fun()//狗的特有功能{System.out.println("看家");}}class pig extends animal{void sing(){System.out.println("哼哼叫");}void fun(){System.out.println("拱地");}}public class Main{public static void main(String[] args){//以前指揮對象做事/* * cat 小貓 = new cat(); 小貓.sing();*/animal a = new cat();//自動型別提升,貓對象提升到了動物,類似byte x = 3;int y = x;a.sing();//PS:貓一旦提升到了動物,但是其特有功能無法訪問。//專業說法,向上轉型。目的:限制對特有功能的訪問//如果還行用貓的特有功能//就可以將該對象,向下轉型cat c = (cat)a;//將動物a,向下轉型為了貓cc.fun();// 向下轉型的目的:是為了使用子類中特有的方法 /*animal d = new animal(); * animal f = new dog(); * cat g = (cat)f;cat e = (cat)d;這種類型不允許,小動物就一定是貓麼*/}}
注意:對於轉型,自始至終都是子類對象在做著類型的轉化:貓對象一會轉型為動物,一會轉型為貓
PS:轉型是有目的性
練習:
/* * BLF和BLF2的故事 * BLF2是BLF的兒子 * */class BLF{void 功能(){System.out.println("用C++寫程式");}void 說英語(){System.out.println("hello,world");}}class BLF2 extends BLF{void 功能(){System.out.println("用java寫程式");}void 說漢語(){System.out.println("你好,世界");}}public class Main{public static void main(String[] args){BLF x = new BLF2();//一天BLF2冒充BLFx.功能();//只能是用java寫程式,因為BLF2隻會javax.說英語();//可以,讓BLF2像BLF一樣,說英語//x.說漢語();//不可以,BLF2已經向上轉型為BLF,禁止了BLF2特有功能的使用BLF2 Z = (BLF2)x;//變回來Z.說漢語();}}
五、類型判斷:
instanceof 用法:import java.lang.reflect.Method;abstract class animal{abstract void sing(); }class cat extends animal{void sing(){System.out.println("喵喵叫");}void fun(){System.out.println("抓老鼠");}}class dog extends animal{void sing(){System.out.println("汪汪叫");}void fun(){System.out.println("看家");}}public class Main{public static void main(String[] args){animal BLF = new cat();//貓向上轉型為了動物method1(BLF);animal BLF2 = new dog();//貓向上轉型為了動物method2(BLF2);}public static void method1(animal a){a.sing();//a.fun();我們知道向上轉型後,就無法使用子類特有功能//所以想要使用就必須再向下轉型cat c = (cat)a;c.fun();//只是可以的,但是假如我們傳一隻狗呢}public static void method2(animal a){a.sing();if(a instanceof cat)//instancdof用於判斷a的具體類型//通常是在向下轉型前用於健壯性的判斷{cat c = (cat)a;c.fun();}else if(a instanceof dog)//當然一個父類,有n多子類,不可能寫n多if{dog c = (dog)a;c.fun();}else if(a==null){System.out.println("類型錯誤");}}}
instaceof 後面可以是類,也可以是介面,且,它只適用於引用資料類型判斷
六、多態的成員的特點:(面試。。。。。。)
1.成員變數
2.成員函數
3.靜態函數
1.成員變數
編譯時間,參考引用型變數所屬類中是否有調用成員變數,有,編譯通過,沒有,編譯失敗
{ 為什麼編譯失敗?
假如動物類中沒有sing這個方法
animal c = new cat();//把貓提升為動物
c.sing();//貓現在是動物了,但是動物不會sing,所以編譯失敗 }
運行時,參考引用型變數所屬類中是否有調用成員變數,並運行該所屬類中的成員變數
簡單說:就是編譯和運行都參考等號(“=”)左邊,fu f = new zi();
見代碼://本問題不會在開發中出現,只會在面試時。。。
class fu{int num = 3;}class zi extends fu{int num = 4;}public class Main{public static void main(String[] args){/* zi z = new zi(); * System.out.println(z.num);//普通繼承是先找子類,子類有的直接覆蓋 * *///多態形式fu f = new zi();//向上轉型為了父類System.out.println(f.num);//3//想要列印子類的成員變數,向下轉型zi ff = (zi)f;System.out.println(ff.num);//4}}
2.成員函數(非靜態函數,重點)
編譯時間,參考引用型變數所屬類中是否有調用的函數,有,編譯通過,沒有,編譯失敗
運行時,參考對象所屬類中是否有調用的函數,並運行該所屬類中的函數
簡單說:編譯看左邊,運行看右邊
(非靜態函數,需要用對象調用,所以運行時看右邊)
class fu{void show(){System.out.println("父");}}class zi extends fu{void show(){System.out.println("子");}}public class Main{public static void main(String[] args){fu f = new zi();//向上轉型將子類型隱藏f.show();//列印“子”,如果把子類show注釋,列印父}}
3.靜態函數(可以直接用類名調用,比較特殊)
實際上多態性應該沒有靜態函數,對象的多態性,而靜態函數,可以直接用類名調用,建立對象來調用靜態方法,這個對象其實就是垃圾
編譯時間,參考引用型變數所屬類中是否有調用的靜態方法,有,編譯通過,沒有,編譯失敗
運行時,參考引用型變數所屬類中是否有調用的函數,並運行該所屬類中的函數
簡單說:就是編譯和運行都看左邊
class fu{static void my(){System.out.println("父sta");}}class zi extends fu{static void my(){System.out.println("子sta");}}public class Main{public static void main(String[] args){fu f = new zi();f.my();//列印“父sta”,如果刪掉父類中的my方法,編譯失敗}}
java 多態的應用
因為a 是C的執行個體,
B b=(B)a; 這個是把a賦值給b,其中B只是聲明類,
A a=new C();a是C的執行個體,所以b.f1();;首先是調用C的方法,加入在C中沒有找到合適的方法,才去聲明類裡B中去找合適的方法
還有 class C extends B 因為C是B的子類,所以C的對象調用的方法f1已經覆蓋了B中的方法f1
java多態
是問多態的概念嗎?
多態性是指允許不同類的對象對同一訊息作出響應。多態性包括參數化多態性和包含多態性。多態性語言具有靈活、抽象、行為共用、代碼共用的優勢,很好的解決了應用程式函數同名問題。
多態有兩種表現形式:重載和覆蓋
首先說重載(overload),是發生在同一類中。與什麼父類子類、繼承毫無關係。
標識一個函數除了函數名外,還有函數的參數(個數和類型)。也就是說,一個類中可以有兩個或更多的函數,叫同一個名字而他們的參數不同。
他們之間毫無關係,是不同的函數,只是可能他們的功能類似,所以才命名一樣,增加可讀性,僅此而已!
再說覆蓋(override),是發生在子類中!也就是說必須有繼承的情況下才有覆蓋發生。
我們知道繼承一個類,也就有了父類了全部方法,如果你感到哪個方法不爽,功能要變,那就把那個函數在子類中重新實現一遍。
這樣再調用這個方法的時候,就是執行子類中的過程了。父類中的函數就被覆蓋了。(當然,覆蓋的時候函數名和參數要和父類中完全一樣,不然你的方法對父類中的方法就不起任何作用,因為兩者是兩個函數,毫不關係)