對象多態性的理解

面向對象的三大特性:封裝—保護類中的屬性不被外部直接訪問到;繼承—擴展類的屬性和功能;那麼多態性呢?
多態性是Java中最強悍的地方,那麼有一個簡單但是又需要好好推敲的疑問:什麼是多態?什麼是對象的多態?
1,什麼是多態?
,按我的理解,什麼是多態,字面意思咯,多種狀態。
,面向對象的多態特性有兩種提現方式:1,方法的重載與覆寫(有的人說這不算多態,然而,我感覺算,因爲他們也滿足了多種狀態的要求);2,對象的多態性;
重載—根據方法參數個數和參數類型的不同完成的功能也不同;覆寫—–子類根據需求覆寫父類中的方法;


1-0:對象的多態性
1-1相上轉型:
使用父類對象接收子類實例(自動完成)。子類對象爲父類對象實例化。
發生了向上轉型,那麼在子類中那些獨有的屬性和方法,就不能被這個轉型了的對象所看到了。
class A{                    // 定義類A
    public void fun1(){     // 定義fun1()方法
        System.out.println("A --> public void fun1(){}") ;
    }
    public void fun2(){
        this.fun1() ;       // 調用fun1()方法
    }
};
class B extends A{
    public void fun1(){     // 此方法被子類覆寫了
        System.out.println("B --> public void fun1(){}") ;
    }
    public void fun3(){    //子類重新定義的自己獨有的方法
        System.out.println("B --> public void fun3(){}") ;
    }
};
public class PolDemo01{
    public static void main(String asrgs[]){
        B b = new B() ;     // 實例化子類對象
        A a = b ;           // 向上轉型關係
        a.fun1() ;          // 此方法被子類覆寫過
        a.fun3() ;
    }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 
這裏a.fun3();這句代碼就會報錯,因爲a已經是從b向上轉型過來的了,它看不到b獨有的方法了,能看到的都是b中從a繼承而來和覆寫而來的方法和屬性。

向上轉型中的注意點
但是,注意,發生了向上轉型,是父類對象,然後去調用那些父類中的方法,會發現,被調用的依然是子類中覆寫過的方法,如果沒有覆寫過這些方法,則纔會調用父類中的方法,因爲發生向上轉型了,一定程度上說,他還是子類實例,在我的理解裏,是剝除了子類獨有特性的子類。(可以形象的這麼理解,這個子類對象是個私生子,是婢女所生的非正室所生的孩子,沒有給予他開疆拓土成爲一方諸侯的能力,僅僅是擁有其父輩積攢的餘蔭以及和父輩緊密相關的權力,只有正室所生的孩子才擁有父輩帶來的光輝以及在父輩基礎上繼續開疆拓土的權力)調用子類獨有的方法和屬性就會報錯了,因爲他的這些方法屬性,已經被剝除了,他看不到了,所有報錯了。

public class PolDemo01{
    public static void main(String asrgs[]){
        B b = new B() ;     // 實例化子類對象
        A a = b ;           // 向上轉型關係
        a.fun1() ;          // 此方法被子類覆寫過
        a.fun2() ;
    }
};
1
2
3
4
5
6
7
8


1-2向下轉型:
使用子類對象接收父類對象,就是將父類對象變爲子類對象(需要強制轉型)。
class A{                    // 定義類A
    public void fun1(){     // 定義fun1()方法
        System.out.println("A --> public void fun1(){}") ;
    }
    public void fun2(){
        this.fun1() ;       // 調用fun1()方法
    }
};
class B extends A{
    public void fun1(){     // 此方法被子類覆寫了
        System.out.println("B --> public void fun1(){}") ;
    }
    public void fun3(){
        System.out.println("B --> public void fun3(){}") ;
    }
};
public class PolDemo02{
    public static void main(String asrgs[]){
        A a = new B() ;         // 向上轉型關係
        B b = (B)a ;        // 發生了向下轉型關係
        b.fun1() ;
        b.fun2() ;
        b.fun3() ;
    }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25


向下轉型中,有一個注意點:
在進行向下轉型時,必須先進行向上轉型,否則會出現類型轉換異常(ClassCastException)。因爲,僅僅通過父類,是無法判斷某個類是否是其子類的,但是從子類中就可以直接通過extend關鍵字,直接明白其父類是誰,如果兩個沒有關係的對象之間進行轉換就會發生此異常,就是說,要發生對象的向下轉型關係,則肯定必須先產生一個向上轉型關係,這樣的目的就是爲了建立兩個對象之間的關係。
class A{                    // 定義類A
    public void fun1(){     // 定義fun1()方法
        System.out.println("A --> public void fun1(){}") ;
    }
    public void fun2(){
        this.fun1() ;       // 調用fun1()方法
    }
};
class B extends A{
    public void fun1(){     // 此方法被子類覆寫了
        System.out.println("B --> public void fun1(){}") ;
    }
    public void fun3(){
        System.out.println("B --> public void fun3(){}") ;
    }
};
public class PolDemo03{
    public static void main(String asrgs[]){
        A a = new A() ;         // 實例化了一個父類對象
        B b = (B)a ;        // 發生了向下轉型關係
        b.fun1() ;
        b.fun2() ;
        b.fun3() ;
    }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25


比如 A 類,B extends A 類,首先第一中情況:B b=new A();這肯定會報異常的;然後第二種情況:A a=new B();這就是向上轉型,就是爲了讓A,B兩個類的對象產生關係;之後在進行向下轉型,B b=(B)a;這就不會有問題了;
2,對象多態性的優勢?
說道優勢,怎麼最明顯,必然是比較一下,一個東西,一門技術到底好不好,怎麼知道,對比一下,俗話說,沒比較就沒傷害,對比一下,使用前和使用後到底有什麼差別就知道優勢在哪裏了。
2-1問題:
設計一個方法,此方法可以接收A類的任意子類對象,並在主類中調用該方法。
首先不使用多態,這個問題,肯定需要使用重載來完成:

class A{                    // 定義類A
    public void fun1(){     // 定義fun1()方法
        System.out.println("A --> public void fun1(){}") ;
    }
    public void fun2(){
        this.fun1() ;       // 調用fun1()方法
    }
};
class B extends A{
    public void fun1(){     // 此方法被子類覆寫了
        System.out.println("B --> public void fun1(){}") ;
    }
    public void fun3(){
        System.out.println("B --> public void fun3(){}") ;
    }
};
class C extends A{
    public void fun1(){     // 此方法被子類覆寫了
        System.out.println("C --> public void fun1(){}") ;
    }
    public void fun5(){
        System.out.println("C --> public void fun5(){}") ;
    }
};
public class PolDemo04{
    public static void main(String asrgs[]){
        fun(new B()) ;  // 傳遞B的實例
        fun(new C()) ;  // 傳遞B的實例
    }
    public static void fun(B b){
        b.fun1() ;      // 調用覆寫父類中的fun1()方法
    }
    public static void fun(C c){
        c.fun1() ;      // 調用覆寫父類中的fun1()方法
    }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
如果現在擴展功能,A類,有1000個子類,那就麻煩了,要增加很多個重載的功能方法,很不好。

使用對象的多態

class A{                    // 定義類A
    public void fun1(){     // 定義fun1()方法
        System.out.println("A --> public void fun1(){}") ;
    }
    public void fun2(){
        this.fun1() ;       // 調用fun1()方法
    }
};
class B extends A{
    public void fun1(){     // 此方法被子類覆寫了
        System.out.println("B --> public void fun1(){}") ;
    }
    public void fun3(){
        System.out.println("B --> public void fun3(){}") ;
    }
};
class C extends A{
    public void fun1(){     // 此方法被子類覆寫了
        System.out.println("C --> public void fun1(){}") ;
    }
    public void fun5(){
        System.out.println("C --> public void fun5(){}") ;
    }
};
public class PolDemo05{
    public static void main(String asrgs[]){
        fun(new B()) ;  // 傳遞B的實例
        fun(new C()) ;  // 傳遞B的實例
    }
    public static void fun(A a){
        a.fun1() ;      // 調用覆寫父類中的fun1()方法
    }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
這樣的代碼,無論A類有多少個子類都沒問題。這個功能方法不需要進行修改。

總結: 
 
多態性總結。

3,instanceof關鍵字
3-1,什麼時候使用instanceof關鍵字,這個關鍵字有什麼作用?
其實對於這個問題,很簡單,在Java中,可以通過instanceof關鍵字來判斷,一個對象是哪一個類的實例。

格式: 對象 instanceof 類名稱 —>返回內容是一個Boolean結果。
對於之前的問題:一個方法可以接受一個類的任意子類對象,並可以調用該方法的問題已經通過多態的向上轉型得到了完美的解決;那麼現在有一個新的問題:A的子類A extends B,繼承了A的方法,同時自定義了一個新的方法fun3(),A extends C,自定義了一個新的方法fun5(),現在的需求是:——當傳入的類的類的對象是B的實例的時候,讓這個對象調用fun3,如果傳入的對象是C的實例的時候就讓它調用fun5,

對於這個需求,僅僅通過參數的多態就不夠了,還需要在方法內部通過使用instanceof關鍵字對傳入的對象進行判斷,然後分別執行對應的方法。
    class A{                    // 定義類A
        public void fun1(){     // 定義fun1()方法
            System.out.println("A --> public void fun1(){}") ;
        }
        public void fun2(){
            this.fun1() ;       // 調用fun1()方法
        }
    };
    class B extends A{
        public void fun1(){     // 此方法被子類覆寫了
            System.out.println("B --> public void fun1(){}") ;
        }
        public void fun3(){
            System.out.println("B --> public void fun3(){}") ;
        }
    };
    public class InstanceofDemo01{
        public static void main(String asrgs[]){
            A a1 = new B() ;        // 通過向上轉型實例化對象   
            System.out.println("A a1 = new B():" + (a1 instanceof A)) ;     //true
            System.out.println("A a1 = new B():" + (a1 instanceof B)) ;     //true
            A a2 = new A() ;        // 通過向上轉型實例化對象
            System.out.println("A a2 = new A():" + (a2 instanceof A)) ;   //true
            System.out.println("A a2 = new A():" + (a2 instanceof B)) ;    //FALSE
        }
    };
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
在開發中,對於向下轉型,一定要進行轉型驗證,以避免classCastException。

class A{                    // 定義類A
    public void fun1(){     // 定義fun1()方法
        System.out.println("A --> public void fun1(){}") ;
    }
    public void fun2(){
        this.fun1() ;       // 調用fun1()方法
    }
};
class B extends A{
    public void fun1(){     // 此方法被子類覆寫了
        System.out.println("B --> public void fun1(){}") ;
    }
    public void fun3(){
        System.out.println("B --> public void fun3(){}") ;
    }
};
class C extends A{
    public void fun1(){     // 此方法被子類覆寫了
        System.out.println("C --> public void fun1(){}") ;
    }
    public void fun5(){
        System.out.println("C --> public void fun5(){}") ;
    }
};
public class InstanceofDemo02{
    public static void main(String asrgs[]){
        fun(new B()) ;
        fun(new C()) ;
    }
    public static void fun(A a){
        a.fun1() ;
        if(a instanceof B){
            B b = (B) a ;
            b.fun3() ;
        }
        if(a instanceof C){
            C c = (C) a ;
            c.fun5() ;
        }
    }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
如果現在要新增子類,則肯定要修改fun方法,這樣一來就失去了程序的靈活性, 
所以,開發中,程序設計的重點要放在父類的設計上,只有父類設計的足夠合理, 
開發過程纔會非常方便,就想基石必須夯實,高樓才能更高。

開發規則: 
一個類永遠不要去繼承一個已經實現好的類,而是最好去繼承一個抽象類或者去實現一個接口。
--------------------- 
作者:費城之鷹 
來源:CSDN 
原文:https://blog.csdn.net/jakezhang1990/article/details/68557710 
版權聲明:本文爲博主原創文章,轉載請附上博文鏈接!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章