• 类的继承和多态细思


    类的继承和多态细思

    一、前言

      类的继承和多态光靠概念和想象是不够的,我们需要编码去分析之,因此我使用代码来说明一些很难分清的东西。

    二、分析代码

    A类代码:

    package zyr.study.poly;
    
    public class A {
        public A(){
            System.out.println("初始化A...");
        }
    
        public String show(A obj) {  
            return ("A and A");  
        }   
        
        public String show(B obj) {  
            return ("A and B");  
        }   
        
        public String show(D obj) {  
            return ("A and D");  
        }  
    }

    B类代码:

    package zyr.study.poly;
    
    public class B extends A{
        public  B(){
            System.out.println("初始化B...");
        }
    
        public String show(A obj){  
            return ("B and A");  
        }   
        
        public String show(B obj){  
            return ("B and B");  
        }  
    }

    C类代码:

    package zyr.study.poly;
    
    public class C extends B{
        public C(){
            System.out.println("初始化C...");
        }
    }

    D类代码:

    package zyr.study.poly;
    
    public class D extends B{
        public D(){
            System.out.println("初始化D...");
        }
    }

    main函数:

     1 package zyr.study.poly;
     2 
     3 
     4 public class Main {
     5 
     6     public static void main(String[] args) {  
     7         A a1 = new A();  
     8         System.out.println("-----------------");  
     9         A a2 = new B();  
    10         System.out.println("-----------------");  
    11         B b = new B();  
    12         System.out.println("-----------------");  
    13         C c = new C();  
    14         System.out.println("-----------------");  
    15         D d = new D();  
    16         System.out.println("-----------------");  
    17           
    18         System.out.println("1--" + a1.show(a1));  // a and a
    19         System.out.println("2--" + a1.show(a2));  // a and a
    20         System.out.println("3--" + a1.show(b));   // a and b
    21         System.out.println("4--" + a1.show(c));   // a and b
    22         System.out.println("5--" + a1.show(d));   // a and d
    23         
    24         System.out.println("-----------------");  
    25         
    26         System.out.println("1--" + a2.show(a1));  // b and a
    27         System.out.println("2--" + a2.show(a2));  // b and a
    28         System.out.println("3--" + a2.show(b));   // b and b
    29         System.out.println("4--" + a2.show(c));   // b and b
    30         System.out.println("5--" + a2.show(d));   // a and d
    31         
    32         System.out.println("-----------------");  
    33         
    34         System.out.println("1--" + b.show(a1));  // b and a
    35         System.out.println("2--" + b.show(a2));  // b and a
    36         System.out.println("3--" + b.show(b));   // b and b
    37         System.out.println("4--" + b.show(c));   // b and b
    38         System.out.println("5--" + b.show(d));   // a and d
    39     }  
    40 }  

    运行结果:

    初始化A...
    -----------------
    初始化A...
    初始化B...
    -----------------
    初始化A...
    初始化B...
    -----------------
    初始化A...
    初始化B...
    初始化C...
    -----------------
    初始化A...
    初始化B...
    初始化D...
    -----------------
    1--A and A
    2--A and A
    3--A and B
    4--A and B
    5--A and D
    -----------------
    1--B and A
    2--B and A
    3--B and B
    4--B and B
    5--A and D
    -----------------
    1--B and A
    2--B and A
    3--B and B
    4--B and B
    5--A and D
    View Code

      下面我们仔细分析这个程序和运行结果。

      首先我们定义了一个类,命名为A,这是一个超类或者叫做基类。在这个类中,使用了函数级别的多态或者说是重载,分别能识别的类型为A类、B类(A类的子类),D类(B类的子类,A类的孙类);

      然后我们定义了B类,继承自A类,同时重写了A类和B类;

      之后我们定义了C类,继承自B类,无操作;

      然后我们定义了D类,继承自B类,无操作;

      在函数体中,我们使用了各自的类定义了对象,并且有一个特殊的,那就是A类的引用指向了B类的对象,这就是多态了,这样的结果就是,A a2=new B();a2所指的函数中因为B继承自A,除了B中重写A中的方法之外,B中其他的方法都是不可见的,并且A中除了被重写的方法之外的方法是可见的。

      让我们看第一批分析:

    1         System.out.println("1--" + a1.show(a1));  // a and a
    2         System.out.println("2--" + a1.show(a2));  // a and a
    3         System.out.println("3--" + a1.show(b));   // a and b
    4         System.out.println("4--" + a1.show(c));   // a and b
    5         System.out.println("5--" + a1.show(d));   // a and d

      对于第一行,a1的定义是A类的引用,并且指向A的对象,因此可见范围为A类中的A,B,D,因此,只有在A中的方法才能被选择,所以本身作为参数A,则选A and A;对于第二行,a2的定义还是A类的,只不过包含的东西多了一点而已(其实也不多,只是包含了B中重写A中的方法,而不包含A中被重写的方法),因此底子里还是A类的,只会认A,结果为A and A;对于第三行,b是货真价实的,则选择A and B;对于第四行,C的对象就看关系的远近了,在符合条件的所有方法中,找一个距离C血缘关系最近的类,那就是B类了,因为A中没有C类的方法,因此A and B;对于第五行,A中正好有D类的方法,因此血缘关系肯定是最近了,选择自身A and D。

      再来看第二批函数:

    1         System.out.println("1--" + a2.show(a1));  // b and a
    2         System.out.println("2--" + a2.show(a2));  // b and a
    3         System.out.println("3--" + a2.show(b));   // b and b
    4         System.out.println("4--" + a2.show(c));   // b and b
    5         System.out.println("5--" + a2.show(d));   // a and d

      首先,a2是个变态,经过了一定的改变,按照上面的分析,我们知道此时函数的可见性是A and D;B and A;B and B;

      因此第一行,找距离A类血缘最近的,肯定是B and A了;第二行,a2本质上还是A的引用,因此依旧是B and A;第三行,B找到自己血缘关系最近的B and B;第四行,C依旧寻找距离自己关系最近的,B and B;第五行,D寻找到了A and D;

      最后看看第三批函数:

    1         System.out.println("1--" + b.show(a1));  // b and a
    2         System.out.println("2--" + b.show(a2));  // b and a
    3         System.out.println("3--" + b.show(b));   // b and b
    4         System.out.println("4--" + b.show(c));   // b and b
    5         System.out.println("5--" + b.show(d));   // a and d

      可见性:B是货真价实的,因此A and D;B and A;B and B。对于第一行,A类血缘最近的是B and A;第二行,a2也是A,因此B and A;第三行,b选择B and B;第四行,C选择血缘最近的B and B;第五行,D选择A and D。

      下面我们将B类里面加入一个C的函数,其他的不变,可见性会发生改变,我们分析一下结果:

     1 package zyr.study.poly;
     2 
     3 public class B extends A{
     4     public  B(){
     5         System.out.println("初始化B...");
     6     }
     7 
     8     public String show(A obj){  
     9         return ("B and A");  
    10     }   
    11     
    12     public String show(B obj){  
    13         return ("B and B");  
    14     }  
    15  
    16     public String show(C obj){  
    17         return ("B and C");  
    18     }  
    19 }

       对于第一批函数,因为可见性的原因,B的修改不能影响结果;对于第二批函数,多了一个C函数,可是我们知道多态的性质,a2所指的函数中C函数依旧是不可见的,因此不影响最终结果。对于第三批函数,可见性也发生了改变,在原有基础上多了C函数,因此第三批第四行中结果变成B and C,这从侧面上证明我们的分析是正确的。

    运行结果如下:

    初始化A...
    -----------------
    初始化A...
    初始化B...
    -----------------
    初始化A...
    初始化B...
    -----------------
    初始化A...
    初始化B...
    初始化C...
    -----------------
    初始化A...
    初始化B...
    初始化D...
    -----------------
    1--A and A
    2--A and A
    3--A and B
    4--A and B
    5--A and D
    -----------------
    1--B and A
    2--B and A
    3--B and B
    4--B and B
    5--A and D
    -----------------
    1--B and A
    2--B and A
    3--B and B
    4--B and C
    5--A and D
    View Code

      对于构造函数的初始化,我们可以看到都是先初始化最超类,然后次超类,一直到自身的,如果将超类的构造函数设为私有,将提示错误,否则没问题。

    三、总结

      在编程中,我们要注意定义中引用所指对象的可见性,先分析可见性,分析的时候要注意重载(override)和重写(overwrite)的区别,重写就是覆盖,重载有可能被排除在外,一定要记得。还有函数的初始化以及变量的可见性,都是值得分析的,如果再加上静态变量,静态函数,一切都变得有意思了。

  • 相关阅读:
    Photoshop做32位带Alpha通道的bmp图片
    PNG怎么转换成32位的BMP保持透明
    解决WIN32窗口不响应WM_LBUTTONDBLCLK消息
    Windows键盘消息处理
    对象与控件如何建立关联
    DrawItem
    在C语言中除法运算为什么没有小数部分?
    使用GDI+进行图片处理时要注意的问题
    MFC中无标题栏窗口的移动
    MFC带标题栏的窗口和不带标题栏的窗口最大化
  • 原文地址:https://www.cnblogs.com/zyrblog/p/9219011.html
Copyright © 2020-2023  润新知