JAVA随堂笔记课【九】:多态、抽象

多态 polymorphism

多态三要素:

  1. 继承环境下,子类重写父类方法。

    Public Son extends Dad;

  2. 通用父类引用变量指向子类对象。

    Dad p = new Son();

  3. 恰好调用的是子类的重写方法。

在多态的前提下,一个父类变量可以指向任意一个子类。
当父类调用该子类重写过的方法时,执行的是子类的重写代码,而不是父类。
这种做法可以让一个父类灵活运用所有的子类同名方法,提高变量使用率。进一步可以扩展成工厂模式。

说了这么多,用图片来解释一下可能更好理解。接下来要你做一件事:在图中找到所有的sound方法。

如图,三个sound方法分别存在于Animal、Tiger、ChineseTiger三个类中。

Animal a = new Animal();
a.sound();
a = new Tiger();
a.sound();
a = new ChineseTiger();
a.sound();

问:三次sound分别执行的是哪一个sound?
答案是:Animal.sound,Tiger.sound和ChineseTiger.sound。

我在“继承类在内存中变量的指向问题”(http://blog.csdn.net/sqrt31/article/details/50378686)说过,父类在指向子类的同时会将其视作父类,子类部分无法调用。

在刚才的代码中,父类指向了子类,将其视作父类,按理说子类部分无法调用,那么为什么第2、3个a.sound执行的依然是子类的函数呢?

自相矛盾的原因

其实原因并不复杂。虽然父类将子类视作父类。但是在要执行方法的时候,程序依然是从外到内的。
从图中不难看出,视作父类需要加载的方法,只有Animal下的sound和run。然而,这个Animal类却被无数的子类嵌套了。现在,我们需要调用Animal的sound。

还记得在第八节课说过的吗?在调用的时候,从外向内一层一层找,找到就开始执行。这句话还特意加粗了。

那么这次调用的时候我们找的是什么?就是sound。

所以你从外向内找,碰到的第一个sound方法,就算是子类重写的,父类也拿过来用。

这种做法就是大名鼎鼎的——多态。

多态的作用

看起来像是不管不顾,从外向内找,只要找到了,不管是不是自己的……反正我就用了你怎么地吧。
在我明白前面这些东西的时候,我总有一种“多态是老程序员们阴错阳差制造出来的产物”的感觉……不过,这种似乎在内存中蛮不讲理的做法,在JAVA中却可以大战拳脚。

就拿刚才的Animal类来说——我有了这个Animal类,我是不是可以用Animal类型的变量,开辟一块可以盛放任何Animal的子类的空间?

这种做法,让一个父类变量,就可以在所有子类的同名方法中畅通无阻,这么多子类都有父类的重名方法(比如,刚才的sound),那作为父类,老子想用谁就用谁。超级万金油。

利用多态重写toString

toString是Object类的变量。而Object是任何类都能继承到的类。
这意味着什么?你的任何一个变量xxx,不管是什么类都可以进行xxx.toString()操作。这碉堡了。
你可以试试看对任何一个类型的变量p进行如下的输出测试:

System.out.println(p.toString());

然后你会在命令行窗口看到类似如下的输出:

work.Polymorphism.Tester.Son@1be847c

当然有可能不太一样,不过格式都一样的,领会精神。

我们来看一下Object中的toString类。

public String toString() {
    return getClass().getName() + "@" + Integer.toHexString(hashCode());
}

现在明白了吧,获取你的当前类名,再获取hashCode。
hashCode,现在作为初学者,姑且当成内存地址吧,暂时未尝不可。

那么,根据多态的理论,我们可以在外面的子类——也就是任意调用了Object类的类——也就是任意类,进行重写。

抽象 abstract

什么是抽象类

首先,抽象类渴望被继承。
因为,抽象类不能构造实例。

原因很简单,抽象类是未完工的类。一个没完工的类怎么能投入使用呢?当然不能。
嗯……那一个不能使用的类怎么用?
用法只有一个——你可以继承它啊。然后把它没完成的方法,在你的子类里完成^_^这样抽象类不就可以发挥作用了吗。

事实上,抽象类渴望被继承。
一个没有子类继承的抽象类,不能构造并且根本就没人调用它,在程序运行中的作用是零。

下面的代码新建了一个抽象类A,并把未完成的抽象方法A.m3()交给子类B完成了。

abstract class A{
    void m1(){
        ...;
    }
    void m2(){} //空方法也是完成的方法,只不过不进行任何操作。
    abstract void m(); //抽象方法,未完成的方法。
}

class B extends A{
    void m3(){}
}

普通类,Final类,abstract类的区别

操作 普通类 Final类 abstract类
创建实例 yes yes no
创建子类(继承) yes no yes
作为引用变量数据类型 yes yes yes

注:抽象类可以作为引用变量数据类型,但是不能new对象出来,需要子类对其继承,然后new子对象才行。

主动抽象类和被动抽象类

被动抽象类(A)存在未完成的抽象方法(m3),因此被迫成为抽象类。

主动抽象类的所有方法都已经完成,但是故意在开头加了abstract前缀,自愿剥夺了投入使用的权利,成为等待子类继承的抽象类,这种类称为主动抽象类。

比如,如果从父类Animal派生出Tiger、Wolf、Lion等子类,那么,Animal类可以考虑做成主动抽象类,把代码实现交给子类完成。

你可能感兴趣的:(JAVA随堂笔记课【九】:多态、抽象)