1. 程式人生 > >轉:多型,向上向下造型(https://blog.csdn.net/lingang1991/article/details/69905944)

轉:多型,向上向下造型(https://blog.csdn.net/lingang1991/article/details/69905944)

一、多型是繼封裝、繼承之後,面向物件的第三大特性。現實事物經常會體現出多種形態,如學生,學生是人的一種,則一個具體的同學張三既是學生也是人,即出現兩種形態。  Java作為面向物件的語言,同樣可以描述一個事物的多種形態。如Student類繼承了Person類,一個Student的物件便既是Student,又是Person。一個Student物件既可以賦值給一個Student型別的引用,也可以賦值給一個Person型別的引用。 最終多型體現為父類引用變數可以指向子類物件:父類型別 變數名 = new 子類型別();  1、多型的前提是必須有子父類關係或者類實現介面關係,否則無法完成多型。  2、在使用多型後的父類引用變數呼叫方法時,會呼叫子類重寫後的方法。  二、多型的三種形式:  1、 普通類多型定義的格式  父類 變數名 = new 子類();

    class Fu {}
    class Zi extends Fu {}
    //類的多型使用
Fu f = new Zi();

2、 抽象類多型定義的格式

abstract class Fu {
     public abstract void method();
         }
class Zi extends Fu {
public void method(){
     System.out.println(“重寫父類抽象方法”);
}
}
//類的多型使用
Fu fu= new Zi();

3、介面多型定義的格式

interface Fu {
             public abstract void method();
}
class Zi implements Fu {
             public void method(){
              System.out.println(“重寫介面抽象方法”);
}
}
//介面的多型使用
Fu fu = new Zi();

注意:同一個父類的方法會被不同的子類重寫。在呼叫方法時,呼叫的為各個子類重寫後的方法。

Person p1 = new Student();
   Person p2 = new Teacher();
   p1.work(); //p1會呼叫Student類中重寫的work方法
   p2.work(); //p2會呼叫Teacher類中重寫的work方法

三、掌握了多型的基本使用後,那麼多型出現後類的成員有啥變化呢?前面學習繼承時,我們知道子父類之間成員變數有了自己的特定變化,那麼當多型出現後,成員變數在使用上有沒有變化呢?  多型出現後會導致子父類中的成員變數有微弱的變化。看如下程式碼:

class Fu {
    int num = 4;//沒有這句會編譯失敗
}
class Zi extends Fu {
    int num = 5;
}
class Demo {
    public static void main(String[] args)  {
        Fu f = new Zi();
        System.out.println(f.num);
        Zi z = new Zi();
        System.out.println(z.num);
    }
}

列印結果:4  5  總結:當子父類中出現同名的成員變數時,多型呼叫該變數時:  1、編譯時期:參考的是引用型變數所屬的類中是否有被呼叫的成員變數。沒有,編譯失敗。  2、執行時期:也是呼叫引用型變數所屬的類中的成員變數。  簡單記:編譯執行都參考等號的左邊。編譯執行看左邊

多型出現後會導致子父類中的成員方法有微弱的變化,看程式碼:

class Fu {
    int num = 4;
    //沒有這個方法,編譯失敗
    void show() {
        System.out.println("Fu show num");
    }
}
class Zi extends Fu {
    int num = 5;
    //重寫父類方法
    void show() {
           System.out.println("Zi show num");
    }
    void show_1{
           System.out.println("Zi show show_1");
    }
}
class Demo {
    public static void main(String[] args)  {
        Fu f = new Zi();
        f.show();
        //f.show_1();
    }
}

列印結果:Zi show num  總結:多型成員方法  1、編譯時期:參考引用變數所屬的類,如果沒有類中沒有呼叫的方法,編譯失敗(如果把f.show_1()前面的註釋開啟,則編譯失敗)。  2、執行時期:參考引用變數所指的物件所屬的類,並執行物件所屬類中的成員方法(如果把子類重寫的show()方法註釋掉,那麼列印的結果是Fu show num)。  簡而言之:編譯看左邊,執行看右邊。

四、多型的轉型分為向上轉型與向下轉型兩種  1、向上轉型:當有子類物件賦值給一個父類引用時,便是向上轉型,多型本身就是向上轉型的過程。  使用格式:父類型別 變數名 = new 子類型別();  如:Person p = new Student();  2、向下轉型:一個已經向上轉型的子類物件可以使用強制型別轉換的格式,將父類引用轉為子類引用,這個過程是向下轉型。如果是直接建立父類物件,是無法向下轉型的!  使用格式:  子類型別 變數名 = (子類型別) 父類型別的變數;  如:Person p = new Student();  Student stu = (Student) p  五、多型的好處與弊端  當父類的引用指向子類物件時,就發生了向上轉型,即把子類型別物件轉成了父類型別。向上轉型的好處是隱藏了子類型別,提高了程式碼的擴充套件性。但向上轉型也有弊端,只能使用父類共性的內容,而無法使用子類特有功能,功能有限制。看如下程式碼:

//描述動物類,並抽取共性eat方法
abstract class Animal {
    abstract void eat();
}

// 描述狗類,繼承動物類,重寫eat方法,增加lookHome方法
class Dog extends Animal {
    void eat() {
        System.out.println("啃骨頭");
    }

    void lookHome() {
        System.out.println("看家");
    }
}

// 描述貓類,繼承動物類,重寫eat方法,增加catchMouse方法
class Cat extends Animal {
    void eat() {
        System.out.println("吃魚");
    }

    void catchMouse() {
        System.out.println("抓老鼠");
    }
}

public class Test {
    public static void main(String[] args) {
        Animal a = new Dog(); //多型形式,建立一個狗物件
        a.eat(); // 呼叫物件中的方法,會執行狗類中的eat方法
        // a.lookHome();//使用Dog類特有的方法,需要向下轉型,不能直接使用

        // 為了使用狗類的lookHome方法,需要向下轉型
// 向下轉型過程中,可能會發生型別轉換的錯誤,即ClassCastException異常
        // 那麼,在轉之前需要做健壯性判斷 
        if( !a instanceof Dog){ // 判斷當前物件是否是Dog型別
                System.out.println("型別不匹配,不能轉換"); 
                return; 
        } 
        Dog d = (Dog) a; //向下轉型
        d.lookHome();//呼叫狗類的lookHome方法
    }
}

我們來總結一下:  1、什麼時候使用向上轉型:  當不需要面對子類型別時,通過提高擴充套件性,或者使用父類的功能就能完成相應的操作,這時就可以使用向上轉型。  如:

Animal a = new Dog();
a.eat();

2、什麼時候使用向下轉型  當要使用子類特有功能時,就需要使用向下轉型。  如:

Dog d = (Dog) a; //向下轉型
d.lookHome();//呼叫狗類的lookHome方法

3、向下轉型的好處:可以使用子類特有功能。  4、向下轉型的弊端:需要面對具體的子類物件;在向下轉型時容易發生ClassCastException型別轉換異常。在轉換之前必須做型別判斷。  如:if( !a instanceof Dog){…}