• 多态的概念


    多态在Java中是一个很重要的概念,其成立的条件有三个:

    1. 子类继承父类;
    2. 子类重写父类的方法;
    3. 父类引用指向子类对象。

           前两条大家都清楚,第三条父类引用指向子类对象我多说一句。Java中数据分为基本类型和引用类型,引用类型有引用和对象,存放在栈中的为引用或者有时候叫句柄,存放在堆中的我们称为对象。


    下面我们用例子来说明Java中多态的概念:

    我们定义两个类,Person 和 Student 类,让Student 继承Person类

    public class Person {
                int var = 100;
    
                public void a() {
                    System.out.println("class Person , method a()");
                }
            }
    public class Student extends Person {
                int var = 200;
    
                /*
                 * public void a() {
                 * System.out.println("class Student , method a()"); }
                 */
    
                public void b() {
                    System.out.println("class Student , method b()");
                }
            }

    这里我们先不重写父类的 a() 方法,接下来我们让父类(Person)的引用指向子类(Student)的对象

    public class Test {
                public static void main(String[] args) {
                    Person p = new Student(); // 相当于基本类型的隐式转换
                    p.a();
                }
            }

     

    在这里我们用 p 调用 a() 方法时,执行的显然是父类的 a() 方法,因为子类没有 a() 方法,输出

    class Person , method a()

    如果我们将Student类中 a() 方法的注释去掉(第5行至第8行),在 p 调用 a() 方法的时候执行的为子类重写之后的 a() 方法,输出

    class Student , method a()

    但是,如果这里我们在 main() 方法中调用 p.b() 方法,答案是编译器会报错。那为什么 p 可以调用子类重写的 a() 方法,却不能调用子类的 b() 方法呢?我们可以用天一时代老师的一个形象的例子来说明这个问题。


           我们开头说引用类型分为引用对象两个部分,可以把引用比喻成电视的遥控器,把对象就比作成电视节目。那么 Person p = new Student(); 这句话是不是就相当于用一个父类的或者说是先前的,旧的遥控器来控制一台子类的或者说是新的电视啊。对,就是这样。这么一来,如果说原来的旧电视只有一个 a() 节目的话,我们旧的遥控器就只需要一个按钮就可以控制这台旧电视了,如果说在新电视中不去更新 a() 这个旧的节目,即不重写 a() 方法,那么显然遥控器一打开旧的电视看到的就是旧的 a() 节目,如果说新电视更新了旧的 a() 节目那么自然父类的 a() 节目就被取代了。这时,如果这里我们在新的电视中增加了一个节目 b() 节目,我们想看 b() 节目,能不能看呢,当然不能,因为我们用的是旧的遥控器啊,它只有一个按钮,换不了台啊!

     

           那现在如果我们一定要看新增加的节目怎么办呢?答案就是我们需要换新的遥控器,即 Student s = (Student)p; 这样我们把旧遥控器换成了新遥控器,当然可以看新增加的节目了。

    public class Test {
                public static void main(String[] args) {
                    Person p = new Student(); // 相当于基本类型的隐式转换
                    // p.a();
                    Student s = (Student) p; // 向下转型(强制转换)
                    s.b();
                }
            }

    现在,我们增加一个类

    public class TestBinding {
                public void test(Person p) {
                    p.a();
                }
            }

    这样我们在 main() 方法中这样调用时,能否知道执行的 a() 方法是父类的方法还是子类的方法?

    public class Test {
                public static void main(String[] args) {
                    TestBinding t = new TestBinding();
                    t.test(p);
                }
            }

    答案是不知道,因为我们不知道传进去的是父类的对象还是子类的对象(不知道是新电视还是旧电视)。这里,如果我们传进去父类对象

    public static void main(String[] args) {
            TestBinding t = new TestBinding();
            t.test(new Person());
        }

           执行的就是父类的 a() 方法,因为是旧电视,如果传进去的是子类对象,则执行子类的 a() 方法。就是说我们要用哪个方法不是在编译期的时候决定的,而是在运行期间决定的,这种运行期绑定就叫动态绑定,或者叫后期绑定,或者叫多态


    最后,我们来简单说一下另一种绑定------前期绑定。我们在TestBinding类中增加一行代码,来打印类中的属性

    public class TestBinding {
            public void test(Person p) {
                p.a();
                System.out.println(p.var); // 新增
            }
        }

           那么在main() 方法中传入Person对象和Student对象的时候打印出来的属性值会是多少呢?试过之后我们知道打印的都是 100, 即父类的属性值。原因是Java中属性和静态方法都是在编译期绑定的,即前期绑定。至于静态方法的情况,读者可以自己验证一下。

  • 相关阅读:
    [LOJ2288][THUWC2017]大葱的神力:搜索+背包DP+费用流+随机化
    [省选练习]S
    [省选练习]P
    动态淀粉质(划掉)题单&简要题解
    [CF1093G]Multidimensional Queries:线段树
    [BZOJ3199][SDOI2013]escape:半平面交
    计算几何模板
    [洛谷P5106]dkw的lcm:欧拉函数+容斥原理+扩展欧拉定理
    [POJ1637]Sightseeing tour:混合图欧拉回路
    重复旋律:后缀数组+后缀自动机
  • 原文地址:https://www.cnblogs.com/yuxiaoqi/p/2754185.html
Copyright © 2020-2023  润新知