假設現在有個需求是:
做一個動物園中各個動物開始吃東西的程序
需求分析:
- 定義各種動物對象,動物有名稱等信息以及吃東西的方法;
- 定義動物園對象,動物園有名稱等信息;
- 實例化出不同的動物對象,並放進動物園,開始吃東西。
代碼部分:
//定義 貓 對象
class Cat{
private String name; //貓的名稱
private String color; //貓的顏色
Cat(String name,String color){ //定義帶參構造方法,傳入貓的名稱和特徵
this.name = name;
this.color = color;
};
public void eat() { //重寫父類叫的方法,單獨爲貓設置吃食物
System.out.println("貓吃食物。。。。。");
}
}
//定義 狗 對象
class Dog{
private String name;//名稱
private String type;//種類
Dog(String name,String type){ //定義帶參構造方法,傳入狗的名稱和特徵
this.name = name;
this.type = type;
};
public void eat() { //重寫父類叫的方法,單獨爲狗設置吃食物
System.out.println("狗吃食物。。。。。");
}
}
//定義一個 動物園 類
class Zoo{
private String name; //動物園名稱
Zoo(String name){ //定義帶參構造方法
this.name = name;
};
public void eat(Cat cat) //讓貓開始吃東西
{
cat.eat(); //調用貓的吃東西的方法
}
public void eat(Dog dog) //讓狗開始吃東西
{
dog.eat(); //調用狗的吃東西的方法
}
}
啓動程序,開始吃食物:
public static void main(String[] args) {
Cat cat = new Cat("小花","花色");//new一隻貓
Dog dog = new Dog("小黑","藏獒");//new一隻狗
Zoo zoo = new Zoo("大動物園");//new一個動物園
zoo.eat(cat);//貓在動物開始吃東西
zoo.eat(dog);//狗在動物園開始吃東西
}
執行結果:
這樣就完成需求中的 動物園的動物在吃東西。
但是:
如果還有其他動物呢,比如獅子、老虎、孔雀、駱駝.......等等 ,我們就需要在Zoo類中爲每個動物定義不同的吃東西方法。
現在我們使用Java中的繼承來優化一下代碼:
- 因爲貓、狗、獅子、老虎等都屬於動物,因此我們可以定義一個動物對象(類型),將動物共有的特性使用動物這個對象(類)來表示:
//定義一個 動物 類
class Animal{
private String name; //動物共有的屬性 name
Animal(String name){ //聲明帶參構造方法
this.name = name;
};
public void eat() //動物共有的 吃食物 的方法
{
System.out.println("吃東西。。。。。。");
}
}
2.貓、狗等繼承自動物(類):
//定義 貓 對象
class Cat extends Animal{
private String color; //貓的顏色
Cat(String name,String color){ //定義帶參構造方法,傳入貓的名稱和特徵
super(name); //將實參傳遞給父類中的name
this.color = color;
};
}
//定義 狗 對象
class Dog extends Animal{
private String type;//種類
Dog(String name,String type){ //定義帶參構造方法,傳入狗的名稱和特徵
super(name); //將實參傳遞給父類中的name
this.type = type;
};
}
3.這時,動物園對象(類)中寫法是這樣的:
//定義一個 動物園 類
class Zoo{
private String name; //動物園名稱
Zoo(String name){ //定義帶參構造方法
this.name = name;
};
public void eat(Animal animal) //動物開始吃東西
{
animal.eat(); //調用動物吃東西的方法
}
}
4.開始執行:
public static void main(String[] args) {
Cat cat = new Cat("小花","花色");//new一隻貓
Dog dog = new Dog("小黑","藏獒");//new一隻狗
Zoo zoo1 = new Zoo("大動物園");//將貓放進動物園
zoo1.eat(cat);//貓開始吃東西
zoo1.eat(dog);//貓開始吃東西
}
5.執行結果:
這樣我們就解決了,需要重複爲每個動物定義eat()吃東西這個操作了。
但是:
由於吃eat()這個方法是“動物”對象提供的,因此不能很好的區分哪個動物在吃,我們的需求是要清楚那個動物在吃,象最初代碼執行結果那樣,該怎麼做呢?
解決方法就是,每個動物重寫一下“動物”對象的吃eat()方法,也就是重寫父類方法:
/定義 貓 對象
class Cat extends Animal{
private String color; //貓的顏色
Cat(String name,String color){ //定義帶參構造方法,傳入貓的名稱和特徵
super(name);
this.color = color;
};
@Override
public void eat() { //重寫父類吃的方法,重新爲小貓設置吃食物
System.out.println("貓吃食物。。。。。");
}
}
//定義 狗 對象
class Dog extends Animal{
private String type;//種類
Dog(String name,String type){ //定義帶參構造方法,傳入狗的名稱和特徵
super(name);
this.type = type;
};
@Override
public void eat() { //重寫父類吃的方法,重新爲小狗設置吃食物
System.out.println("狗吃食物。。。。。");
}
}
其他類不變(動物、動物園)
執行結果爲:
好了 大功告成!
這時大家的疑惑可能就是
1.爲什麼在動物園對象中我們eat()參數爲Animal(動物),而傳遞的實參是Cat對象或Dog對象;
2.爲什麼在動物園對象中我們eat()調用的是Animal(動物)的eat(),實際執行出來的確實Cat對象和Dog對象的eat().
解答:
1.因爲我們的Animal對象是其他(Cat對象/Dog對象)的父類,
而java中有這樣一個特性:父類引用可以指向子類對象,也就是說Animal對象中的eat()中的參數Animal對象是一個引用,當我們傳遞其字類對象時,就表示我們將這個父類引用指向了一個子類對象,Java這樣設計的好處就是降低代碼耦合性,提升代碼可擴展性(如:我們後續添加需要其他動物對象,只要其是Animal(動物類)的子類,那麼我們的動物園對象中的代碼是不用改變的)
2.因爲Java有多態,我們的子類(Cat/Dog)繼承自父類並重寫了父類(Animal)中eat(),那麼在調用父類(Animal)中eat()時,在運行期Java會通過判斷我們傳入的是哪個子類對象,再去確定調用對應的eat()(如果子類沒有重寫父類方法,就默認調用父類的eat()),這個就是Java的多態也就是動態綁定。
至此得出:
多態的前提條件:
1.要有繼承;
2.要有重寫;
3.父類引用指向子類對象