package duotai;
public class Animal {
public void move() {
System.out.println("動物在移動");
}
}
package duotai;
public class Cat extends Animal {
public void move() {
System.out.println("貓在走貓步");
}
public void catchMouse() {
System.out.println("貓抓老鼠");
}
}
package duotai;
public class Bord extends Animal {
public void move() {
System.out.println("鳥在飛行");
}
public void fly() {
System.out.println("飛飛飛飛");
}
}
package duotai;
/**
* 關(guān)于iava語言當(dāng)中的多態(tài)語法機制:
1、Animal、Cat、Bird三個類之間的關(guān)系:
Cat維承Animal Bird繼承Animal
Cat和Bird之間沒有任何繼承關(guān)系
2、面向?qū)ο笕筇卣?封裝、繼承、多態(tài)
3、關(guān)于多態(tài)中涉及到的幾個概念:
向上轉(zhuǎn)型(upcasting):子類型-->父類型,又被稱為:自動類型轉(zhuǎn)換。
向下轉(zhuǎn)型(downdasting),父類型->子類型,又被稱為:強制類型轉(zhuǎn)換。【需要加強制類型轉(zhuǎn)換符】
需要記憶:
無論是向上轉(zhuǎn)型還是向下轉(zhuǎn)型,兩種類型之間必須要有繼承關(guān)系。沒有繼承關(guān)系,程序是無法編譯通過的。
*
*
*/
public class Test {
public static void main(String[] args) {
Animal animal = new Animal();
animal.move();
Cat cat = new Cat();
cat.move();
cat.catchMouse();
Bord bord = new Bord();
bord.move();
/**
* newCat()創(chuàng)建的對象的類型是cat,animal2這個引用的數(shù)據(jù)類型是Animal,可見它們進行了類型轉(zhuǎn)換
子類型轉(zhuǎn)換成父類型,稱為向上轉(zhuǎn)型/upcasting,或者稱為自動類型轉(zhuǎn)換。
Java中允許這種語法:父類型引用指向子類型對象。
*/
Animal animal2 = new Cat();
/**
1、java程序永遠都分為編譯階段和運行階段。
2、先分析編譯階段,再分析運行階段,編譯無法通過,根本是無法運行的。
3、編譯階段編譯器檢查animal2這個引用的數(shù)據(jù)類型為Animal,由于Animal.class
字節(jié)碼當(dāng)中有move()方法,所以編譯通過了。這個討程我們成為靜態(tài)綁定,編譯階段綁定。只有靜態(tài)綁定成功之后才有后續(xù)的運行。
4、在程序運行階段,JVM堆內(nèi)存當(dāng)中真實創(chuàng)建的對象是cat對象,那么以下程序在運行階段一定會調(diào)用Cat對象的move()方法,
此時發(fā)生了程序的動態(tài)綁定,運行階段綁定。
5、無論是Cat類有沒有重寫move方法,運行階段一定調(diào)用的是Cat對象的move方法,因為底層真實對象就是Cat對象。
6、父類型引用指向子類型對象這種機制導(dǎo)致程序存在編譯階段綁定和運行階段綁定兩種不同的形態(tài),這種機制可以成為一種多態(tài)語法機制。
*/
animal2.move();
/**
因為編譯階段編譯器檢查到a2的類型是Animal類型,
從Animal.class字節(jié)碼文件當(dāng)中查找catch()方法,最終沒有找到該方法,導(dǎo)致靜態(tài)綁定失敗,沒有綁定成功,
也就是說編譯失敗了。別談運行了。
a2.catchMouse();
*/
/**
需求:
假設(shè)想讓以上的對象執(zhí)行catohMouse()方法
animal2是無法直接調(diào)用的,因為animal2的類型Animal,Animal中沒有catch()方法。
我們可以將animal2強制類型轉(zhuǎn)換為Cat類型。
animal2的類型是Animal(父類),轉(zhuǎn)換成Cat類型(子類),被稱為向下轉(zhuǎn)型/downcasting制類型轉(zhuǎn)換。
注:向下轉(zhuǎn)型也需要兩種類型之間必須有繼承關(guān)系。不然編譯報錯。強制類型轉(zhuǎn)換需要加強制類型轉(zhuǎn)換符。
什么時候需要使用向下轉(zhuǎn)型呢?
當(dāng)調(diào)用的方法是子類型中特有的,在父類型當(dāng)中不存在,必須進行向下轉(zhuǎn)型。
*/
Cat cat2 = (Cat)animal2;
cat2.catchMouse();
//父類型引用指向子類型對象【多態(tài)】
Animal animal3=new Bord();
/** 1、以下程序編譯是沒有問題的,因為編譯器檢查到a3的數(shù)據(jù)類型是Animal, Animal和Cat之間存在繼承關(guān)系,
* 并且Animal是父類型,Cat是子類型,父類型轉(zhuǎn)換成子類型叫做向下轉(zhuǎn)型,語法合格。
2、程序雖然編譯通過了,但是程序在運行階段會出現(xiàn)異常,因為JVM堆內(nèi)存當(dāng)中真實存在的對象是Bird類型,
Bird對象無法轉(zhuǎn)換成Cat對象,因為兩種類型之間不存在任何繼承關(guān)系,此時出現(xiàn)了著名的異常:
java.lang.ClassCastException
類型轉(zhuǎn)換異常,這種異常總是在"向下轉(zhuǎn)型"的時候會發(fā)生。
Cat cat3 =(Cat)animal3;
*/
/**
1、以上異常只有在強制類型轉(zhuǎn)換的時候會發(fā)生,也就是說"向下轉(zhuǎn)型"存在隱患(編譯過了,但是運行錯了!)
2、向上轉(zhuǎn)型只要編譯通過,運行一定不會出問題:Animal animal=new Cat()
3、向下轉(zhuǎn)型編譯通過,運行可能錯誤:Animal animal3=new Bird();Cat cat3=(Cat)animal3;
4、怎么避免向下轉(zhuǎn)型出現(xiàn)的ClassCastException呢?
使用instanceof運算符可以避免出現(xiàn)以上的異常。
5、instanceof運算符怎么用?
5.1、語法格式:
(引用 instanceof 數(shù)據(jù)類型名)
5.2、以上運算符的執(zhí)行結(jié)果類型是布爾類型,結(jié)果可能是true/false
5.3、關(guān)于運算結(jié)果true/false:
假設(shè):(a instanceof Animal)
true表示: a這個引用指向的對象是一個Animal類型。
false表示:a這個引用指向的對象不是一個Animal類型。
6、Java規(guī)范中要求:在進行強制類型轉(zhuǎn)換之前,建議采用instanceof運算符進行判斷,
避免ClassCastException異常的發(fā)生。這是一種編程好習(xí)慣。
Animal a1 = new Cat();
Animal a2 = new Bord();
if (a1 instanceof Cat) {
Cat c1 = (Cat)a1;
}
if (a2 instanceof Bord) {
Bord b1 = (Bord)a2;
}
*/
if (animal3 instanceof Cat) {
Cat cat3 =(Cat)animal3;
cat3.catchMouse();
}else if(animal3 instanceof Bord) {
Bord bord2 = (Bord)animal3;
bord2.fly();
}
}
}