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("====我是小狗我負責看家");
    }
}

 

歡迎繼續關注下一篇,在接口實現中的實現多態


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM