超詳細Java基礎-多態

茫茫人海千千萬萬,感謝這一秒你看到這裏。但願個人能對你的有所幫助!共勉!java

願你在將來的日子,保持熱愛,奔赴山海!編程

Java基礎知識(多態)

多態

多態就是指程序中定義的引用變量所指向的具體類型和經過該引用變量發出的方法調用在編程時並不肯定,而是在程序運行期間才肯定,即一個引用變量到底會指向哪一個類的實例對象,該引用變量發出的方法調用究竟是哪一個類中實現的方法,必須在由程序運行期間才能決定。學習

由於在程序運行時才肯定具體的類,這樣,不用修改源程序代碼,就可讓引用變量綁定到各類不一樣的類實現上,從而致使該引用調用的具體方法隨之改變,即不修改程序代碼就能夠改變程序運行時所綁定的具體代碼,讓程序能夠選擇多個運行狀態,這就是多態性。測試

多態的定義和存在的必要條件

多態的定義code

  • 多態是指同一個行爲具備多個不一樣表現形式或形態的能力。
  • 多態就是同一個接口,使用不一樣的實例而執行不一樣操做。

就舉動物類的例子吧,cat和dog都是屬於動物這一類,而動物呢,都有一個共同的行爲就是吃吧,而不一樣的動物所吃的食物都大不相同吧!對象

呢,它喜歡吃魚blog

而對於狗呢,它就比較喜歡啃骨頭繼承

因此多態就是對於吃這一行爲來講,每種動物對吃這一行爲所表現的行爲都不盡相同。接口

多態存在的三個必要條件io

  1. 繼承或者實現

    在多態中必須存在有繼承或者實現關係的子類和父類。

  2. 方法的重寫

    子類對父類中某些方法進行從新定義(重寫),在調用這些方法時就會調用子類的方法。

  3. 基類引用指向派生類對象,即父類引用指向子類對象

    父類類型:指子類對象繼承的父類類型,或者實現的父接口類型。

多態的格式:

父類類型 變量名 = new 子類類型();

變量名.方法名();

多態格式能夠充分體現了同一個接口,使用不一樣的實例而執行不一樣操做。

接下來咱們具體來進行案例體會體會吧!

多態的案例

多態咱們首先要知道的一點:

當使用多態方式調用方法時,首先檢查父類中是否有該方法,若是沒有,則編譯錯誤;若是有,執行的是子類重寫後方法。若是子類沒有重寫該方法,就會調用父類的該方法

總結起來就是:編譯看左邊,運行看右邊。

首先咱們先定義一個父類動物類,動物有吃的行爲!

接着定義一個貓類和狗類去繼承動物類,重寫裏面的吃行爲!

具體代碼以下

定義動物父類:

package com.nz.pojo;

/**
 * 先定義一個父類 --> 動物類
 * 動物都有一個吃的行爲屬性
 */
public class Animal {

    public void eat() {
        System.out.println("動物它們都會吃東西!!!");
    }
}

定義貓咪子類:

package com.nz.pojo;

/**
 * 定義貓類繼承動物類,
 * 隨之重寫裏面的吃行爲,由於貓也有吃的行爲,可是貓喜歡吃罐頭
 */
public class Cat extends Animal{

    public void eat() {
        System.out.println("小喵咪都喜歡吃魚罐頭!");
    }
}

定義小狗子類:

package com.nz.pojo;

/**
 * 定義狗類繼承動物類,
 * 隨之重寫裏面的吃行爲,由於狗也有吃的行爲,可是狗喜歡啃骨頭
 */
public class Dog extends Animal{

    public void eat() {
        System.out.println("小狗狗都愛啃骨頭!");
    }
}

定義測試類,測試多態的形式:

package com.nz;

import com.nz.pojo.Animal;
import com.nz.pojo.Cat;
import com.nz.pojo.Dog;

/**
 * 測試多態的形式
 */
public class Demo {
    public static void main(String[] args) {

        // 多態形式,建立貓類對象
        Animal animal = new Cat();
        // 調用的是Cat的 eat
        animal.eat();

        // 多態形式,建立狗類對象
        Animal animal2 = new Dog();
        // 調用的是Dog的eat
        animal2.eat();
    }
}

獲得的結果:

小喵咪都喜歡吃魚罐頭!
小狗狗都愛啃骨頭!

類的大體結構:

能夠看出咱們可使用多態的屬性獲得不一樣的動物的一個吃的行爲屬性!

多態的好處

提升了代碼的拓展性,使用父類類型做爲方法形式參數,傳遞子類對象給方法,進行方法的調用。

具體咱們來看看吧:

繼續使用上述的動物類、貓類、狗類吧。

定義測試類:

package com.nz;

import com.nz.pojo.Animal;
import com.nz.pojo.Cat;
import com.nz.pojo.Dog;

/**
 * 測試多態的好處
 */
public class Demo2 {
    public static void main(String[] args) {
        // 建立貓和狗對象
        Cat cat = new Cat();
        Dog dog = new Dog();

        // 調用catEat
        catEat(cat);
        // 調用dogEat
        dogEat(dog);

		/*
        多態的好處:
            以上各個動物的吃的方法, 咱們均可以使用animalEat(Animal a)方法來代替。
            而且執行效果都同樣, 因此咱們可使用animalEat直接替代了不一樣動物的吃方法。
        */
        animalEat(cat);
        animalEat(dog);
    }

    /*
        定義幾個不一樣吃的方法,看看具體調用後的結果是什麼吧!
     */
    public static void catEat (Cat cat){
        cat.eat();
    }

    public static void dogEat (Dog dog){
        dog.eat();
    }

    public static void animalEat (Animal animal){
        animal.eat();
    }
}

執行結果:

小喵咪都喜歡吃魚罐頭!
小狗狗都愛啃骨頭!
小喵咪都喜歡吃魚罐頭!
小狗狗都愛啃骨頭!

能夠看出,因爲多態的特性,咱們的animalEat()方法傳入的Animal類型參數,而且它是咱們的Cat和Dog的父類類型,父類類型接收子類對象,因此咱們能夠將Cat對象和Dog對象,傳遞給animalEat()方法。

因此咱們能夠徹底使用animalEat()方法來替代catEat()方法和dogEat()方法,達到一樣的效果!以致於咱們能夠沒必要再單獨寫xxxEat()方法來傳入指定的動物參數了,從而實現了實現類的自動切換。

因此多態的好處體如今:可使咱們的程序編寫的更簡單,並有良好的擴展性。

多態的弊端

從上面的多態的好處,能夠看到咱們可使用父類的參數代替了某個子類的參數,從而達到程序的擴展!

可是對於某個子類有些獨有的功能方法時,此時咱們的多態的寫法就沒法訪問子類獨有功能了

具體來瞧瞧?

代碼以下:

從新定義下貓的子類:

package com.nz.pojo;

/**
 * 定義貓類繼承動物類,
 * 隨之重寫裏面的吃行爲,由於貓也有吃的行爲,可是貓喜歡吃罐頭
 */
public class Cat extends Animal{

    public void eat() {
        System.out.println("小喵咪都喜歡吃魚罐頭!");
    }

    /**
     * 增長一哥貓咪特有的玩球方法()
     */
    public void playBall() {
        System.out.println("小喵咪都喜歡小球!");
    }
}

定義測試類:

package com.nz;

import com.nz.pojo.Animal;
import com.nz.pojo.Cat;

/**
 * 測試多態的弊端!
 */
public class Demo3 {
    public static void main(String[] args) {
        Animal animal = new Cat();

        animal.eat();

        animal.playBall();//編譯報錯,編譯看左邊,Animal沒有這個方法
    }
}

能夠看到動物類和貓類有個共同的eat吃方法,可是呢,貓咪多了個玩球球的方法。而對於動物對象來講,它自己動物類沒有玩球球的方法,因此它的編譯就直接沒有經過了!

那有什麼方法解決呢?且看下一章節吧!

引用類型轉換

1. 引用類型轉換是什麼,爲何須要它?

從上面的多態的弊端的案例中,咱們能夠看到,咱們使用動物對象時沒法直接訪問到貓類中的玩球球方法,這也就是咱們以前說的編譯看左邊,運行看右邊。

而在咱們使用多態方式調用方法時,首先檢查會左邊的父類中是否有該方法,若是沒有,則編譯錯誤。也就表明着,父類沒法調用子類獨有的方法。、

因此說,若是編譯都錯誤,更別說運行了。這也是多態給咱們帶來的一點小困擾,而咱們若是想要調用子類特有的方法,必須作向下轉型。

2. 向上轉型(自動轉換)

對於向下轉型,咱們先來說解下向上轉型的概念吧。

向上轉型

多態自己是子類向父類向上轉換(自動轉換)的過程,這個過程是默認的。當父類引用指向一個子類對象時,即是向上轉型。

對於父類和子類的關係來講,具體來看圖說話:

父類相對與子類來講是大範圍的類型,Animal是動物類,是父類。而Cat是貓咪類,是子類。

因此對於父類Animal來講,它的範圍是比較大的,它包含一切動物,包括貓咪類和小狗類。

因此對於子類類型這種範圍小的,咱們能夠直接自動轉型給父類類型的變量。

使用格式:

父類類型 變量名 = new 子類類型();

如:Animal animal = new Cat();

至關於有:
Animal animal = (Animal) new Cat();

至關於自動幫咱們了一個隱形的轉換爲動物類的一個過程,由於動物自己就包含了貓咪。

3. 向下轉型(強制轉換)

向上轉型能夠知道它是子類自動轉換爲父類的一個過程,因此咱們如今再來看看向下轉型的定義:

向下轉型

向下轉型就是由父類向子類向下轉換的過程,這個過程是強制的。一個須要將父類對象轉爲子類對象,可使用強制類型轉換的格式,這即是向下轉型。

爲何這種就必須本身強制加上一個類型轉換過程呢?

對於父類和子類的關係來講,咱們接着看圖說話:

對於貓咪類的話,它在動物類中只是其中的一部分吧,而對於動物類來講,它有許多其餘子類動物如狗,牛,豬等等。

因此對於動物父類想要向下轉型的時候, 它此時不知道指向那個子類,由於不肯定呀,因此就必須本身加上強制的類型轉換的一個過程。

使用格式:

子類類型 變量名 = (子類類型) 父類變量名;
如:
Animal animal = new Cat();
Cat cat = (Cat) animal;
cat.playBall();// 此時咱們就可使用貓咪的特有方法啦

因此對於多態的弊端,沒法使用子類特有的參數,咱們也解決啦,能夠經過向下轉型的方法,從而將類型強制轉換爲某個子類對象後,再去調用子類的特有方法!

4. 向下轉型的問題

雖然咱們可使用向下轉型使得咱們可使用子類的獨有方法,可是轉型的過程當中,一不當心就會遇到這樣的問題了,來,咱們來看看下面的代碼:

public class Test {
    public static void main(String[] args) {
        // 向上轉型  
        Animal a = new Cat();  
        a.eat();               // 調用的是 Cat 的 eat

        // 向下轉型  
        Dog d = (Dog)a;       
        d.watchHouse();        // 調用的是 Dog 的 watchHouse 【運行報錯】
    }  
}

這段代碼能夠經過編譯,可是運行時,卻報出了 ClassCastException ,類型轉換異常!這是由於,明明建立了Cat類型對象,運行時,固然不能轉換成Dog對象的。

5. 轉型的異常

轉型的過程當中,一不當心就會遇到這樣的問題,請看以下代碼:

定義狗類中額外的獨有遛狗方法:

package com.nz.pojo;

/**
 * 定義狗類繼承動物類,
 * 隨之重寫裏面的吃行爲,由於狗也有吃的行爲,可是狗喜歡啃骨頭
 */
public class Dog extends Animal{

    public void eat() {
        System.out.println("小狗狗都愛啃骨頭!");
    }

    public void walk() {
        System.out.println("小狗在被我溜着!");
    }
}

定義測試類

package com.nz;

import com.nz.pojo.Animal;
import com.nz.pojo.Cat;
import com.nz.pojo.Dog;

/**
 * 測試多態的向下轉型的問題
 */
public class Demo4 {
    public static void main(String[] args) {

        // 向上轉型的過程
        Animal animal = new Cat();

        // 調用了貓咪的吃方法
        animal.eat();

        // 向下轉型
        Dog dog = (Dog) animal;

        dog.walk(); // 調用的是 Dog 的 walk 能夠經過,可是會運行報錯
    }
}

獲得結果:

小喵咪都喜歡吃魚罐頭!
Exception in thread "main" java.lang.ClassCastException: com.nz.pojo.Cat cannot be cast to com.nz.pojo.Dog
	at com.nz.Demo4.main(Demo4.java:20)

咱們能夠看到,雖然咱們的代碼經過編譯,可是終究在運行時,仍是出錯了,拋出了 ClassCastException 類型轉換的異常。

其實咱們能夠知道,咱們在上面的時候,建立了Cat類型對象,而在向下轉型時,將其強行轉換爲了Dog類型,因此程序在運行時,就會拋出類型轉換的異常!

那咱們如何能夠避免這種異常發生呢?且看下一節分析!

6. instanceof關鍵字

Java爲咱們提供一個關鍵字instanceof ,它能夠幫助咱們避免了ClassCastException 類型轉換異常的發生。

那如何作呢?

格式:

變量名 instanceof 數據類型

解釋:

  • 若是變量屬於該數據類型或者其子類類型,返回true。
  • 若是變量不屬於該數據類型或者其子類類型,返回false。

代碼實現:

package com.nz;

import com.nz.pojo.Animal;
import com.nz.pojo.Cat;
import com.nz.pojo.Dog;

/**
 * 使用instanceof解決類型轉換異常!
 */
public class Demo5 {
    public static void main(String[] args) {

        // 向上轉型的過程
        Animal animal = new Cat();

        // 調用了貓咪的吃方法
        animal.eat();

        // 向下轉型
        if (animal instanceof Cat){
            Cat cat = (Cat) animal;
            cat.playBall();        // 調用的是 Cat 的 playBall
        } else if (animal instanceof Dog){
            Dog dog = (Dog) animal;
            dog.walk();       // 調用的是 Dog 的 walk
        }
    }
}

結果:

小喵咪都喜歡吃魚罐頭!
小喵咪都喜歡小球!

能夠發現,它能夠幫助咱們在作類型轉換前,判斷該類型是否屬於該類型或者子類類型,若是是,咱們就能夠強轉啦!

總結

相信各位看官都對Java中的特性之一多態的知識和使用有了必定了解,等待下一次更多Java基礎的學習吧!

學到這裏,今天的世界打烊了,晚安!雖然這篇文章完結了,可是我還在,永不完結。我會努力保持寫文章。來日方長,何懼車遙馬慢!

感謝各位看到這裏!願你韶華不負,青春無悔!

注: 若是文章有任何錯誤和建議,請各位大佬盡情評論留言!若是這篇文章對你也有所幫助,但願可愛親切的您給個關注點贊收藏下,很是感謝啦!

相關文章
相關標籤/搜索