Java 中的多型,一次講個夠之繼承關係中的多型
多型是繼封裝、繼承之後,面向物件的第三大特性。 現實事物經常會體現出多種形態,如學生,學生是人的一種,則一個具體的同學張三既是學生也是人,即出現兩種形態。 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("====我是小狗我負責看家"); } }
歡迎繼續關注下一篇,在介面實現中的實現