多態是繼封裝、繼承之后,面向對象的第三大特性。 現實事物經常會體現出多種形態,如學生,學生是人的一種,則一個具體的同學張三既是學生也是人,即出現兩種形態。 Java作為面向對象的語言,同樣可以描述一個事物的多種形態。如Student類繼承了Person類,一個Student的對象便既是Student,又是Person。 Java中多態的代碼體現在一個子類對象(實現類對象)既可以給這個子類(實現類對象)引用變量賦值,又可以給這個子類(實現類對象)的父類(接口)變量賦值。 如Student類可以為Person類的子類。那么一個Student對象既可以賦值給一個Student類型的引用,也可以賦值給一個Person類型的引用。 最終多態體現為父類引用變量可以指向子類對象。
多態的前提是必須有子父類關系或者類實現接口關系,否則無法完成多態。 在使用多態后的父類引用變量調用方法時,會調用子類重寫后的方法。
文字再怎么講,都不夠生動,直接用代碼來體現
老爸要喝酒,那今天喝什么酒呢,
public class Wine { public void drinkWine(){ System.out.println("===今天我要喝什么酒呢===="); Wine(); } public void Wine(){ System.out.println("===看看俺今天能喝啥子喲===="); } }
public class JNC extends Wine { /** * @desc 子類重載父類方法 * 父類中不存在該方法,向上轉型后,父類是不能引用該方法的 * @param a * @return void */
public void drinkWine(String a){ System.out.println("======今天我要喝劍南春===="); Wine(); } /** * 子類重寫父類方法 * 指向子類的父類引用調用Wine時,必定是調用該方法 */
public void Wine(){ System.out.println("=====劍南春喝上啦,好開森====="); } }
public class Test { public static void main(String[] args) { Wine a = new JNC(); a.drinkWine(); a.Wine(); Wine b = new Wine(); b.drinkWine(); b.Wine(); JNC c= new JNC(); c.drinkWine("qq"); } }

先來看看這一段,
Wine a = new JNC();
a.drinkWine();
a.Wine();
子類劍南春中的drinkWine帶有參數,而父類中的drinkWine不帶有參數,即父類不存在這個方法
運行的時候,調用的是父類的drinkWine,先輸出了
===今天我要喝什么酒呢====
之后繼續調用Wine方法,這個時候是去了子類中,指向子類的父類引用調用Wine時,必定是調用子類中的方法,於是輸出了
=====劍南春喝上啦,好開森=====
上面的Wine和JNC中的方法,都沒有帶Static,如果加上Static呢,看一下代碼和運行的結果
class Wine { public static void drinkWine() { System.out.println("===今天我要喝什么酒呢===="); Wine(); } public static void Wine() { System.out.println("===看看俺今天能喝啥子喲===="); } } class JNC extends Wine { public static void drinkWine(String a) { System.out.println("======今天我要喝劍南春===="); Wine(); } public static void Wine() { System.out.println("=====劍南春喝上啦,好開森====="); } } class Test { public static void main(String[] args) { Wine a = new JNC(); a.drinkWine(); a.Wine(); } }
可以看到,靜態方法,即使向上轉型,也只能調用自己的方法啦
上面比較的是子類和父類的方法,在非靜態方法和靜態方法,父類引用子類的方法,非靜態方法下可以調用子類同名的構造函數方法,不能調用不一樣的構造方法
靜態方法中,子類向上轉型后,父類引用都不能進行調用子類的方法
下面來給父類和子類一些變量,以及一些方法,方法都是非靜態的
父類,定義了一些姓名,年齡,興趣愛好等的變量
和一些say和hobby的方法
public class Father { private String fathername; private int fatherage; private String fahterhobby; public void say() { System.out.println("==我是你爸爸真偉大,養你這么大=="); myhobby(); } public void myhobby() { System.out.println("==我是你爸爸真偉大,只要你媽媽=="); } public Father() { super(); } public Father(String fathername, int fatherage, String fahterhobby) { this.fathername = fathername; this.fatherage = fatherage; this.fahterhobby = fahterhobby; } //省略getters and setters @Override public String toString() { return "Father{" + "fathername='" + fathername + '\'' + ", fatherage=" + fatherage + ", fahterhobby='" + fahterhobby + '\'' + '}'; } public String toString(String fathername, int fatherage, String fahterhobby) { return "Father{" + "fathername='" + fathername + '\'' + ", fatherage=" + fatherage + ", fahterhobby='" + fahterhobby + '\'' + '}'; } }
子類和父類差不多,其實不應該定義一樣的變量,雖然名稱改了一下
public class Son extends Father { public void say(){ System.out.println("==爸爸我要出去玩==="); } public void say(String s){ System.out.println("==爸爸我要出去玩===" +s); } public void myhobby(String aaa){ System.out.println("==爸爸給我買這個玩具: " + aaa); } private String sonname; private int sonage; private String sonhobby; public Son(String sonname, int sonage, String sonhobby) { this.sonname = sonname; this.sonage = sonage; this.sonhobby = sonhobby; } public Son(String fathername, int fatherage, String fahterhobby, String sonname, int sonage, String sonhobby) { super(fathername, fatherage, fahterhobby); this.sonname = sonname; this.sonage = sonage; this.sonhobby = sonhobby; } //省略getters and setters @Override public String toString(String sonname, int sonage, String sonhobby) { return "Son{" + "sonname='" + sonname + '\'' + ", sonage=" + sonage + ", sonhobby='" + sonhobby + '\'' + '}'; } }
測試:
主要測試如下:
子類中,對say()無參數的方法進行了改寫,輸出內容不一致了,且有自己新創建的say(String s)帶有入參的方法
子類對myhobby也新增了,有了入參
子類中對toString方法也帶有入參的
實例化子類對象,父類引用,即向上轉型了,調用say(),這個方法子類中有; 調用myhobby(), 子類中沒有myhobby(), 只有myhobby(String aaa) 然后還要調用toString(), 有參數和無參數的
public class test { public static void main(String[] args) { Father father = new Son("張三",35,"LOL","張四",5,"Learn"); System.out.println("實例化一個Son對象,用父親接收"); father.say(); father.myhobby(); //代碼報錯 // father.myhobby("LOL驚奇娃娃"); System.out.println(father.toString()); System.out.println(father.toString("張三",35,"LOL")); System.out.println("\n"); Son son = new Son("張四",5,"Learn"); System.out.println("實例化一個Son對象,用Son接收"); son.say(); son.say("上海迪士尼"); son.myhobby(); son.myhobby("LOL驚奇娃娃"); System.out.println(son.toString("張四",5,"Learn")); } }
運行結果如下:
1. 父類引用調用say(), 由於子類中有這個方法,調用的是子類的這個方法;
調用myhobby();, 由於子類中沒有這個方法,調用的是父類的這個方法;
調用子類中帶有參數的方法,father.myhobby("LOL驚奇娃娃");代碼直接報錯了
調用toString(),分別是無參數和有參數,因為子類中只有三個有參數的,沒有無參數的,就無參數返回的是父類的,有參數返回的是子類的
2. 子類引用指向子類對象,調用say()無參數的和有參數的,由於子類中都有,都是子類自己的方法進行返回
調用myobby()無參數的和有參數的,由於子類中沒有無參數的,就去爸爸那兒找了找,返回了爸爸的愛好,子類中有帶參數的,就返回了子類自己的
調用toString(3個參數略),就返回了自己的方法,如果調用不帶參數的toString(),就是返回一個父親中的方法了。。。
總結: 父類引用指向子類,調用返回的時候,看看自己家有沒有啊,有啊,哦,不管了,先去兒子家找找,兒子有啊,兒子用你家的,兒子沒有啊,回家用自己的方法吧
子類引用指向子類,調用返回的時候,先去自己(即兒子)方法中看看,我自己沒有呀,去父親方法中看看吧
上面看的繼承中的父子關系是,爸爸有,兒子有,兒子有新的
下面繼續看繼承,論父子之間的關系之,爸爸沒有,兒子有;
員工對象,只有一個mailCheck()方法,定義了一些變量
public class Employee { private String name; private String address; private int number; public Employee(String name, String address, int number) { //System.out.println("Employee 構造函數"); this.name = name; this.address = address; this.number = number; } public void mailCheck() { System.out.println("郵寄支票給: " + this.name + " " + this.address); } @Override public String toString() { return "Employee{" + "name='" + name + '\'' + ", address='" + address + '\'' + ", number=" + number + '}'; } //省略getters and setters
Salary對象繼承了父類
public class Salary extends Employee{ private double yearsalary; // 全年工資 public double getSalary() { return yearsalary; } public void setSalary(double salary) { if(salary >= 0.0) this.yearsalary = salary; } public double computePay() { System.out.println("計算工資,付給:" + getName()); return yearsalary/12; } public Salary(String name, String address, int number, double yearsalary) { super(name, address, number); setSalary(yearsalary); } public void mailCheck() { System.out.println("Salary 類的 mailCheck 方法 "); System.out.println("郵寄支票給:" + getName()+ " ,工資為:" + yearsalary); } }
測試如下
public class Demo { public static void main(String [] args) { Salary s = new Salary("員工 A", "北京", 3, 360000.00); s.mailCheck(); double sa = s.computePay(); System.out.println(sa); System.out.println("\n"); Employee e = new Salary("員工 B", "上海", 2, 240000.00); e.mailCheck(); double salary = ((Salary) e).computePay(); System.out.println(salary); } }
必要要強轉 ((Salary) e).computePay(); 即必須向下轉型,父親引用轉化為子類的,再去調用子類的方法
即回答:論父子之間的關系之,爸爸沒有,兒子有;
本來是爸爸類型的引用,將爸爸類型的引用向下轉型,然后調用
問題來啦,我是父親,我有兩個兒子或者多個兒子呢,鬧啥啊, 爸爸沒有這個方法,兒子們都有呢,咋辦咧
public class Animal { void eat() { System.out.println("Animal"); } } public class Cat extends Animal { public void eat() { System.out.println("===我是貓咪我要吃魚"); } public void work() { System.out.println("===我是貓咪我負責抓老鼠"); } } public class Dog extends Animal { public void eat() { System.out.println("====我是小狗我要吃骨頭"); } public void work() { System.out.println("====我是小狗我負責看家"); } }
public class Test { public static void main(String[] args) { show(new Cat()); // 以 Cat 對象調用 show 方法 System.out.println("\n"); show(new Dog()); // 以 Dog 對象調用 show 方法 System.out.println("\n"); Animal a = new Cat(); // 向上轉型 a.eat(); // 調用的是 Cat 的 eat Cat c = (Cat)a; // 向下轉型 c.work(); // 調用的是 Cat 的 work } public static void show(Animal animal) { animal.eat(); // 類型判斷 if (animal instanceof Cat) { // 貓做的事情 Cat c = (Cat)animal; c.work(); } else if (animal instanceof Dog) { // 狗做的事情 Dog c = (Dog)animal; c.work(); } } }
不如咱直接將動物類改成抽象類吧,
public abstract class Animal { abstract void eat() ; } public class Cat extends Animal { public void eat() { System.out.println("===我是貓咪我要吃魚"); } public void work() { System.out.println("===我是貓咪我負責抓老鼠"); } } public class Dog extends Animal { public void eat() { System.out.println("====我是小狗我要吃骨頭"); } public void work() { System.out.println("====我是小狗我負責看家"); } }
歡迎繼續關注下一篇,在接口實現中的實現多態