java中多態的實現機制
多態的概念:
簡單來說就是事物在運行過程中存在的不同狀態,即父類或接口定義的引用變量指向子類或具體實現類的實例對象。程序調用方法在運行期才進行動態綁定,而不是引用變量的類型中定義的方法。
多態存在的前提:
1、存在繼承關系,子類繼承父類;
2、子類重寫父類的方法;
3、父類引用指向子類對象。
具體實例:
1、定義一個父類:Animal
1 package demo; 2 3 class Animal{ 4 int num = 10; 5 static int age = 20; 6 public void eat() { 7 System.out.println("動物吃飯");8 } 9 public static void sleep() { 10 System.out.println("動物睡覺"); 11 } 12 public void run() { 13 System.out.println("動物奔跑"); 14 } 15 }
2、子類:Cat繼承Animal
1 package demo; 2 3 public class Cat extends Animal{ 4 int num=80; 5 static intage=90; 6 String name="TomCat"; 7 @Override 8 public void eat() { 9 // TODO Auto-generated method stub 10 System.out.println("貓吃飯"); 11 } 12 public static void sleep(){ 13 System.out.println("貓在睡覺"); 14 } 15 public void catchMouse() { 16 // TODO Auto-generated method stub17 System.out.println("貓抓老鼠"); 18 } 19 }
3、測試類:Test1
1 package demo; 2 3 public class Test1 { 4 5 public static void main(String[] args) { 6 Animal am=new Cat(); 7 am.eat(); 8 am.sleep(); 9 am.run(); 10 System.out.println(am.num); 11 System.out.println(am.age); 12 //以下兩行註釋內容稍後解釋 13 //am.catchMouse(); 14 //System.out.println(am.name); 15 } 16 17 }
以上三段代碼充分體現了多態存在的前提條件:
1、存在繼承關系:Cat 類繼承了Animal類;
2、子類要重寫父類方法:子類Cat重寫(override)了父類Animal的兩個方法eat(),sleep(),其中eat()為普通方法,sleep()為靜態方法(static);
3、父類引用指向子類對象:測試類中Animal am=new Cat();,語句在堆內開辟了一塊內存分配給子類(Cat),並把棧內存中的父類(Animal)的引用指向了這個Cat對象。
測試類運行後的結果:
可以看出:
1、子類Cat重寫的父類Animal的普通方法eat()的輸出結果為“貓吃飯”;
2、子類Cat重寫的父類Animal的靜態方法sleep()的輸出結果為“動物睡覺”;
3、未被子類Cat重寫的父類Animal的普通方法run()的輸出結果為“動物奔跑”;
4、子類Cat繼承的父類Animal屬性,輸出結果分別為父類屬性;
5、輸出子類Cat特有屬性和方法則會報錯。
因此,根據以上分析可以總結出多態成員訪問的特點:
Animal am=new Cat();
成員變量:
編譯看左(父類),運行看左(父類);
成員方法:
編譯看左(父類),運行看右(子類),輸入動態綁定;
靜態方法:
編譯看左(父類),運行看左(父類),靜態方法被提升到類級別,算不上重寫,所以訪問還是看父類;
多態以後不能使用子類特有的屬性和方法,在子類Cat中有一個特有的屬性String name="TomCat";並且還有一個特有的抓老鼠的方法catchMouse()。但是在測試類Test1中,嘗試調用子類的特有方法和打印子類特有的屬性時,就會報錯。
那麽如果想要使用子類特有的屬性和方法該怎麽辦呢?可以把這個父類引用指向的子類對象強制轉換為子類Cat類型,這樣am就是子類Cat類型的引用,指向的也是Cat對象了,這樣就能夠使用子類的一些屬性和方法了。
1 package demo; 2 3 public class Test1 { 4 5 public static void main(String[] args) { 6 Animal am=new Cat(); 7 am.eat(); 8 am.sleep(); 9 am.run(); 10 System.out.println(am.num); 11 System.out.println(am.age); 12 //以下兩行註釋內容稍後解釋 13 //am.catchMouse(); 14 //System.out.println(am.name); 15 System.out.println("-------------------------"); 16 Cat ct=(Cat)am; 17 ct.eat(); 18 ct.sleep(); 19 ct.run(); 20 ct.catchMouse(); 21 System.out.println(ct.num); 22 System.out.println(ct.age); 23 System.out.println(ct.name); 24 } 25 26 }
執行強轉語句Cat ct=(Cat)am;後,ct就指向了最開始在堆內存中創建的那個Cat對象了。這就是多態的功能,使用起來十分的靈活,覺少了多余對象的創建,不用為了使用子類的某個方法再去堆內存中開辟一塊新的空間給一個新的子類對象了。
花木蘭替父從軍的例子:
大家都知道花木蘭替父從軍的例子,花木蘭替父親花弧從軍。那麽這時候花木蘭是子類,花弧是父類。花弧有自己的成員屬性年齡,姓名,性別。花木蘭也有這些屬性,但是很明顯二者的屬性值完全不一樣。花弧有自己的非靜態成員方法‘騎馬殺敵’,同樣花木蘭也遺傳了父親一樣的方法‘騎馬殺敵’。花弧還有一個靜態方法‘自我介紹’,每個人都可以問花弧姓甚名誰。同時花木蘭還有一個自己特有的非靜態成員方法‘塗脂抹粉’。但是,現在花木蘭替父從軍,女扮男裝。這時候相當於父類的引用(花弧這個名字)指向了子類對象(花木蘭這個人),那麽在其他類(其他的人)中訪問子類對象(花木蘭這個人)的成員屬性(姓名,年齡,性別)時,其實看到的都是花木蘭她父親的名字(花弧)、年齡(60歲)、性別(男)。當訪問子類對象(花木蘭這個人)的非靜態成員方法(騎馬打仗)時,其實都是看到花木蘭自己運用十八般武藝在騎馬打仗。當訪問花木蘭的靜態方法時(自我介紹),花木蘭都是用她父親的名字信息在向別人作自我介紹。並且這時候花木蘭不能使用自己特有的成員方法‘塗脂抹粉’。-----多態中的向上轉型
那麽終於一將功成萬骨枯,打仗旗開得勝了,花木蘭告別了戰爭生活。有一天,遇到了自己心愛的男人,這時候愛情的力量將父類對象的引用(花弧這個名字)強制轉換為子類對象本來的引用(花木蘭這個名字),那麽花木蘭又從新成為了她自己,這時候她完全是她自己了。名字是花木蘭,年齡是28,性別是女,打仗依然那樣生猛女漢子,自我介紹則堂堂正正地告訴別人我叫花木蘭。終於可以使用自己特有的成員方法‘塗脂抹粉’了。從此,花木蘭完全回到了替父從軍前的那個花木蘭了。-----多態中的向下轉型
向上轉型向下轉型一定是在多態這個前提下,同時向上轉型是安全的,向下轉型則不安全。比如強制將女兒變成父親,則女兒可以使用父親的身份存在,反之,將父親變成女兒,就變成東方不敗了,系統此時就會報錯非法類型轉換。另外開發中一般利用多態聲明形式參數,並將創建子類的匿名對象作為實際參數。
java中多態的實現機制