1. 程式人生 > >java之多態(一)

java之多態(一)

stat span print ive 匹配 anim 註釋 上轉型 exceptio

一、多態是繼封裝、繼承之後,面向對象的第三大特性。現實事物經常會體現出多種形態,如學生,學生是人的一種,則一個具體的同學張三既是學生也是人,即出現兩種形態。
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){…}

java之多態(一)