对象多态性的理解

面向对象的三大特性:封装—保护类中的属性不被外部直接访问到;继承—扩展类的属性和功能;那么多态性呢?
多态性是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 
版权声明:本文为博主原创文章,转载请附上博文链接!

猜你喜欢

转载自blog.csdn.net/writebook2016/article/details/84660784