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;
/**
* 關于iava語言當中的多態語法機制:
1、Animal、Cat、Bird三個類之間的關系:
Cat維承Animal Bird繼承Animal
Cat和Bird之間沒有任何繼承關系
2、面向對象三大特征:封裝、繼承、多態
3、關于多態中涉及到的幾個概念:
向上轉型(upcasting):子類型-->父類型,又被稱為:自動類型轉換。
向下轉型(downdasting),父類型->子類型,又被稱為:強制類型轉換。【需要加強制類型轉換符】
需要記憶:
無論是向上轉型還是向下轉型,兩種類型之間必須要有繼承關系。沒有繼承關系,程序是無法編譯通過的。
*
*
*/
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()創建的對象的類型是cat,animal2這個引用的數據類型是Animal,可見它們進行了類型轉換
子類型轉換成父類型,稱為向上轉型/upcasting,或者稱為自動類型轉換。
Java中允許這種語法:父類型引用指向子類型對象。
*/
Animal animal2 = new Cat();
/**
1、java程序永遠都分為編譯階段和運行階段。
2、先分析編譯階段,再分析運行階段,編譯無法通過,根本是無法運行的。
3、編譯階段編譯器檢查animal2這個引用的數據類型為Animal,由于Animal.class
字節碼當中有move()方法,所以編譯通過了。這個討程我們成為靜態綁定,編譯階段綁定。只有靜態綁定成功之后才有后續的運行。
4、在程序運行階段,JVM堆內存當中真實創建的對象是cat對象,那么以下程序在運行階段一定會調用Cat對象的move()方法,
此時發生了程序的動態綁定,運行階段綁定。
5、無論是Cat類有沒有重寫move方法,運行階段一定調用的是Cat對象的move方法,因為底層真實對象就是Cat對象。
6、父類型引用指向子類型對象這種機制導致程序存在編譯階段綁定和運行階段綁定兩種不同的形態,這種機制可以成為一種多態語法機制。
*/
animal2.move();

/**
因為編譯階段編譯器檢查到a2的類型是Animal類型,
從Animal.class字節碼文件當中查找catch()方法,最終沒有找到該方法,導致靜態綁定失敗,沒有綁定成功,
也就是說編譯失敗了。別談運行了。
a2.catchMouse();
*/

/**
需求:
假設想讓以上的對象執行catohMouse()方法
animal2是無法直接調用的,因為animal2的類型Animal,Animal中沒有catch()方法。
我們可以將animal2強制類型轉換為Cat類型。
animal2的類型是Animal(父類),轉換成Cat類型(子類),被稱為向下轉型/downcasting制類型轉換。
注:向下轉型也需要兩種類型之間必須有繼承關系。不然編譯報錯。強制類型轉換需要加強制類型轉換符。
什么時候需要使用向下轉型呢?
當調用的方法是子類型中特有的,在父類型當中不存在,必須進行向下轉型。
*/
Cat cat2 = (Cat)animal2;
cat2.catchMouse();

//父類型引用指向子類型對象【多態】
Animal animal3=new Bord();
/** 1、以下程序編譯是沒有問題的,因為編譯器檢查到a3的數據類型是Animal, Animal和Cat之間存在繼承關系,
* 并且Animal是父類型,Cat是子類型,父類型轉換成子類型叫做向下轉型,語法合格。
2、程序雖然編譯通過了,但是程序在運行階段會出現異常,因為JVM堆內存當中真實存在的對象是Bird類型,
Bird對象無法轉換成Cat對象,因為兩種類型之間不存在任何繼承關系,此時出現了著名的異常:
java.lang.ClassCastException
類型轉換異常,這種異常總是在"向下轉型"的時候會發生。
Cat cat3 =(Cat)animal3;
*/

/**
1、以上異常只有在強制類型轉換的時候會發生,也就是說"向下轉型"存在隱患(編譯過了,但是運行錯了!)
2、向上轉型只要編譯通過,運行一定不會出問題:Animal animal=new Cat()
3、向下轉型編譯通過,運行可能錯誤:Animal animal3=new Bird();Cat cat3=(Cat)animal3;
4、怎么避免向下轉型出現的ClassCastException呢?
使用instanceof運算符可以避免出現以上的異常。
5、instanceof運算符怎么用?
5.1、語法格式:
(引用 instanceof 數據類型名)
5.2、以上運算符的執行結果類型是布爾類型,結果可能是true/false
5.3、關于運算結果true/false:
假設:(a instanceof Animal)
true表示: a這個引用指向的對象是一個Animal類型。
false表示:a這個引用指向的對象不是一個Animal類型。
6、Java規范中要求:在進行強制類型轉換之前,建議采用instanceof運算符進行判斷,
避免ClassCastException異常的發生。這是一種編程好習慣。

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();
}

}
}