继承环境下,子类重写父类方法。
Public Son extends Dad;
通用父类引用变量指向子类对象。
Dad p = new Son();
在多态的前提下,一个父类变量可以指向任意一个子类。
当父类调用该子类重写过的方法时,执行的是子类的重写代码,而不是父类。
这种做法可以让一个父类灵活运用所有的子类同名方法,提高变量使用率。进一步可以扩展成工厂模式。
说了这么多,用图片来解释一下可能更好理解。接下来要你做一件事:在图中找到所有的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是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类的类——也就是任意类,进行重写。
首先,抽象类渴望被继承。
因为,抽象类不能构造实例。
原因很简单,抽象类是未完工的类。一个没完工的类怎么能投入使用呢?当然不能。
嗯……那一个不能使用的类怎么用?
用法只有一个——你可以继承它啊。然后把它没完成的方法,在你的子类里完成^_^这样抽象类不就可以发挥作用了吗。
事实上,抽象类渴望被继承。
一个没有子类继承的抽象类,不能构造并且根本就没人调用它,在程序运行中的作用是零。
下面的代码新建了一个抽象类A,并把未完成的抽象方法A.m3()交给子类B完成了。
abstract class A{
void m1(){
...;
}
void m2(){} //空方法也是完成的方法,只不过不进行任何操作。
abstract void m(); //抽象方法,未完成的方法。
}
class B extends A{
void m3(){}
}
操作 | 普通类 | Final类 | abstract类 |
---|---|---|---|
创建实例 | yes | yes | no |
创建子类(继承) | yes | no | yes |
作为引用变量数据类型 | yes | yes | yes |
注:抽象类可以作为引用变量数据类型,但是不能new对象出来,需要子类对其继承,然后new子对象才行。
被动抽象类(A)存在未完成的抽象方法(m3),因此被迫成为抽象类。
主动抽象类的所有方法都已经完成,但是故意在开头加了abstract前缀,自愿剥夺了投入使用的权利,成为等待子类继承的抽象类,这种类称为主动抽象类。
比如,如果从父类Animal派生出Tiger、Wolf、Lion等子类,那么,Animal类可以考虑做成主动抽象类,把代码实现交给子类完成。