运行时多态性是面向对象程序设计代码重用的一个最强大机制,动态性的概念也可以被说成“一个接口,多个方法”。Java实现运行时多态性的基础是动态方法调度,它是一种在运行时而不是在编译期调用重载方法的机制,下面就继承和接口实现两方面谈谈java运行时多态性的实现。
一、通过继承中超类对象引用变量引用子类对象来实现
举例说明:
//定义超类superA
class superA
{
int i = 100;
void fun()
{
System.out.println(“This is superA”);
}
}
//定义superA的子类subB
class subB extends superA
{
int m = 1;
void fun()
{
System.out.println(“This is subB”);
}
}
//定义superA的子类subC
class subC extends superA
{
int n = 1;
void fun()
{
System.out.println(“This is subC”);
}
}
class Test
{
public static void main(String[] args)
{
superA a;
subB b = new subB();
subC c = new subC();
a=b;
a.fun(); (1)
a=c;
a.fun(); (2)
}
}
运行结果为:
This is subB
This is subC
上述代码中subB和subC是超类superA的子类,我们在类Test中声明了3个引用变量a, b, c,通过将子类对象引用赋值给超类对象引用变量来实现动态方法调用。也许有人会问:“为什么(1)和(2)不输出:This is superA”。java 的这种机制遵循一个原则:当超类对象引用变量引用子类对象时,被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法,但是这个被调用的方法必须是在超类中定义过的,也就是说被子类覆盖的方法。
所以,不要被上例中(1)和(2)所迷惑,虽然写成a.fun(),但是由于(1)中的a被b赋值,指向了子类subB的一个实例,因而(1)所调用的fun()实际上是子类subB的成员方法fun(),它覆盖了超类superA的成员方法fun();同样(2)调用的是子类subC的成员方法fun()。
另外,如果子类继承的超类是一个抽象类,虽然抽象类不能通过new操作符实例化,但是可以创建抽象类的对象引用指向子类对象,以实现运行时多态性。具体的实现方法同上例。
不过,抽象类的子类必须覆盖实现超类中的所有的抽象方法,否则子类必须被abstract修饰符修饰,当然也就不能被实例化了。
通过方法重写实现多态
一.多态性(如果您不是搞软件的下边有其他更精彩的内容)
在java语言中,多态性体现在两个方面:由方法重载实现的静态多态性(编译时多态)和方法重写实现的动态多态性(运行时多态)。
1) 编译时多态
在编译阶段,具体调用哪个被重载的方法,编译器会根据参数的不同来静态确定调用相应的方法。
2) 运行时多态
由于子类继承了父类所有的属性(私有的除外),所以子类对象可以作为父类对象使用。程序中凡是使用父类对象的地方,都可以用子类对象来代替。一个对象可以通过引用子类的实例来调用子类的方法。
◇ 重写方法的调用原则
(读者朋友请注意此处为重点):java运行时系统根据调用该方法的实例,来决定调用哪个方法。对子类的一个实例,如果子类重写了父类的方法,则运行时系统调用子类的方法;如果子类继承了父类的方法(未重写),则运行时系统调用父类的方法。
一.对象的类型转换
1、子类转换成父类(经典例题)
class A {
public void func1() {
System.out.println("A func1 is calling.");
}
public void func2() {
func1();
}
}
class B extend A {
public void func1() {
System.out.println("B func1 is calling.");
}
public void func3() {
System.out.println("B func3 is calling.");
}
}
class C {
public static void main(String[] args) {
B b = new B();
A a = b;
callA(a);
callA(new B());
}
public void callA(A a) {
a.func1();
a.func2();
}
}
编译器能够自动将类B的实例对象b直接赋值给A类的引用变量,也就是子类能够自动转换成父类类型。另外,程序可以直接创建一个类B的实例对象,传递给需要类A的实例对象作参数的callA()方法,在参数传递的过程中发生了隐式自动类型转换。子类能够自动转换成父类的道理非常容易理解。
难点剖析:(笔者此前查过很多资料费尽周折理解后与大家共享)
问题:为什么类B从类A继承来的func2里调用的func1变成了子类B的func1?
提示:编程的时候眼睛就盯着内存去想问题,而不是盯着程序代码,肯定能想明白这个问题
答案:B类重写了A类的fun1和fun3,所以调用时肯定是子类B的.
而fun2是子类B继承来的,当调用fun2时,是用的父类A的fun2方法,但fun2方法又调用了fun1(这时的fun1就是子类B的了,因为子类B重写了这个方法)
我认为这类问题就是先从子类去看,只要是子类重写了父类的,那么调用的就是子类的,而没有重写的才是调用父类。