所謂多態(tài),就是指程序中定義的引用變量所指向的具體類型和通過該引用變量發(fā)出的方法調(diào)用在編程時并不確定硅急,而是在程序運行期間才確定,即一個引用變量到底會指向哪個類的實例對象,該引用變量發(fā)出的方法調(diào)用的到底是哪個類中實現(xiàn)的方法泻仙,必須在由程序運行期間才能決定。即不修改程序代碼就可以讓程序有多個運行狀態(tài)可以選擇量没,這就是多態(tài)性玉转。同一個事件發(fā)生在不同的對象上會產(chǎn)生不同的結(jié)果,可見殴蹄,多態(tài)實現(xiàn)了很好的擴展性究抓。
多態(tài)的三個必要條件
- 繼承 :在多態(tài)中必須存在有繼承關(guān)系的子類和父類。
- 重寫:子類對父類中某些方法進行重新定義袭灯,在調(diào)用這些方法時就會調(diào)用子類的方法刺下。
- 向上轉(zhuǎn)型:父類引用指向子類對象:Farther f = new Son()
向下轉(zhuǎn)型:
子類引用指向父類引用的子類對象:
語法:Son s = (Son)f
當使用多態(tài)方式調(diào)用方法時,首先檢查父類中是否有該方法稽荧,如果沒有怠李,則編譯錯誤;如果有蛤克,再去調(diào)用子類的同名方法捺癞。
實例的演示:
Test.java 文件代碼:
public class Test {
public static void main(String[] args) {
show(new Cat()); // 以 Cat 對象調(diào)用 show 方法
show(new Dog()); // 以 Dog 對象調(diào)用 show 方法
Animal a = new Cat(); // 向上轉(zhuǎn)型,父類引用指向子類對象
//向上轉(zhuǎn)型時构挤,子類單獨定義的方法會丟失髓介,a.work()會報錯
a.eat(); // 調(diào)用的是 Cat 的 eat
Cat c = (Cat) a; // 向下轉(zhuǎn)型,向下轉(zhuǎn)型前得先向上轉(zhuǎn)型
c.work(); // 調(diào)用的是 Cat 的 work
}
public static void show(Animal a) {
a.eat();
// 類型判斷
if (a instanceof Cat) { // 貓做的事情
Cat c = (Cat) a;
c.work();
} else if (a instanceof Dog) { // 狗做的事情
Dog c = (Dog) a;
c.work();
}
}
}
abstract class Animal { //創(chuàng)建抽象類筋现,該類不能實例化
//即:Animal dog=new Animal 會報錯
abstract void eat(); //抽象方法唐础,只有方法聲明箱歧,沒有方法實現(xiàn)
}
class Cat extends Animal { //普通類繼承抽象類
public void eat() {
System.out.println("吃魚"); //重寫父類中的抽象方法
}
public void work() {
System.out.println("抓老鼠"); //新增方法
}
}
class Dog extends Animal { //普通類繼承抽象類
public void eat() {
System.out.println("吃骨頭"); //重寫父類中的抽象方法
}
public void work() {
System.out.println("看家"); //新增方法
}
}
附:
instanceof 的作用:
因為在多態(tài)發(fā)生時,子類只能調(diào)用父類中的方法(編譯時類型的方法)一膨,而子類自己獨有的方法(運行時類型的方法)無法調(diào)用呀邢,如果強制調(diào)用的話就需要向下轉(zhuǎn)型,語法和基本類型的強制類型轉(zhuǎn)換一樣豹绪;但是向下轉(zhuǎn)型具有一定的風險价淌,很有可能無法成功轉(zhuǎn)化,為了判斷能否成功轉(zhuǎn)化瞒津,就需要 instanceof 先進行一個判斷蝉衣,然后再進行轉(zhuǎn)換操作
instanceof 是一個運算符,語法為:
引用類型變量(object) instanceof 類(class)
功能:
判斷前面的對象是否屬于后面的類巷蚪,或者屬于其子類病毡;
如果是,返回 true屁柏,不是返回 false啦膜;
重寫
當子類對象調(diào)用重寫的方法時,調(diào)用的是子類的方法淌喻,而不是父類中被重寫的方法功戚。
要想調(diào)用父類中被重寫的方法,則必須使用關(guān)鍵字 super似嗤。
Employee.java 文件代碼
public class Employee {
private String name;
private String address;
private int number;
public Employee(String name, String address, int number) {
System.out.println("Employee 構(gòu)造函數(shù)");
this.name = name;
this.address = address;
this.number = number;
}
public void mailCheck() {
System.out.println("Employee 類的 mailCheck 方法 ");
System.out.println("郵寄支票給: " + this.name + " " + this.address);
}
//該方法的作用:獲取到這個類指向的對象的數(shù)據(jù),且按期待的形式輸出
//例:Employee p = new Employee();
//System.out.println(p); 隱式調(diào)用toString(0)方法
//System.out.println(p.toString()); // 顯式調(diào)用toString(0)方法
public String toString() {
return name + " " + address + " " + number;
}
public String getName() {
return name;
}
}
Salary.java 文件代碼:
public class Salary extends Employee {
private double salary; // 全年工資
public Salary(String name, String address, int number, double salary) {
super(name, address, number);
// 關(guān)鍵字super届宠,調(diào)用父類中被重寫的方法烁落,即調(diào)用了父類中的構(gòu)造方法
setSalary(salary);
}
public void mailCheck() {
System.out.println("Salary 類的 mailCheck 方法 ");
System.out.println("郵寄支票給:" + getName()
+ " ,工資為:" + salary);
}
public void setSalary(double newSalary) {
if (newSalary >= 0.0) {
salary = newSalary;
}
}
}
VirtualDemo.java 文件代碼輸出結(jié)果:
public class VirtualDemo {
public static void main(String[] args) {
// 子類引用子類對象
Salary s = new Salary("員工 A", "北京", 3, 3600);
// 父類引用子類對象
Employee e = new Salary("員工 B", "上海", 2, 2400.00);
// Employee e = new Employee("員工 B", "上海", 2);
System.out.println(e);
System.out.println("使用 Salary 的引用調(diào)用 mailCheck -- ");
s.mailCheck();
System.out.println("\n使用 Employee 的引用調(diào)用 mailCheck--");
e.mailCheck();
}
}