多态性也是面向对象程序设计的另一个重要特点。方法的多态,即多种形式,是指属性或方法在子类中表现为多种形态,若以父类定义对象,并动态绑定对象,则该对象的方法将随绑定对象不同而不同。
利用多态可以使程序具有良好的扩展性,程序可以对所有类对象进行通用的处理。Java中实现多态可以通过方法重载实现编译时多态(静态多态),也可以通过对父类成员方法的重写实现运行时多态(动态多态)。
1.编译时多态
编译多态是指在程序编译过程中出现的多态性,可以通过方法重载实现。重载表现为同一个类中方法的多态性,一个类中声明多个重载方法就是为一种功能提供多种实现。编译时,系统根据方法实际参数的数据类型.个数和次序,决定究竟应该执行重载方法中的哪一个。
例如,定义一个四则运算的类,它有一个add()方法用于加法操作,根据不同的使用情况,可以接收整形.实型.字符串.自定义类型(如复数类)等参数。对于每一种加法操作,方法名都为add(),只不过具体实现方式不同,不用另外重新起名,这样大大简化了方法的实现和调用,程序员无须记住很多的方法名,只需传递相应的参数即可。
2.运行时多态
运行时多态是指在程序运行时出现的多态性,可以通过方法重写实现。当子类重写父类方法时,由于子类继承了父类的所有属性和方法,因此,凡是父类对象可以使用的地方,子类对象也可以使用,而且子类还可以重写父类中已有的成员方法,实现父类中没有的其他功能。这就引起了一个问题:在调用某个重写的方法时,到底系统是调用父类的方法还是子类中的方法?这无法在编译时确定,需要系统在运行过程中根据实际来决定,所以这种由方法重写引起的多态称运行时多态。
Java规定,对重写的方法,Java根据调用该方法的实例的类型来决定选择哪个方法。对子类的实例(对象),如果子类重写了父类的方法,则调用子类的方法。如果子类没有重写父类的方法,则调用父类的方法。
package page103;
class Shape{
public void draw() {
System.out.println("draw a Shape");
}
}
class Circle extends Shape{
public void draw() {
System.out.println("draw a Circle");;
}
}
class Ellipse extends Circle{
public void draw() {
System.out.println("draw a Ellipse");
}
}
public class Test {
public static void main(String[] args) {
Shape s=new Shape(); //动态绑定为类Shape对象
Shape c=new Circle(); //动态绑定为类Circle对象
Shape e=new Ellipse(); //动态绑定为类Ellipse对象
s.draw();
c.draw();
e.draw();
}
}
输出结果为:
draw a Shape
draw a Circle
draw a Ellipse
程序分析:
(1)类Shape是父类,类Circle是类Shape的直接子类,类Ellipse是类Circle的直接自乐。这三个类中都定义了draw()方法。子类中的draw()方法覆盖了父类中的同名方法。
(2)主方法中定义了三个对象,三个对象s.c和e都定义为Shape类,但对象s动态绑定为Shape类的对象,对象c动态绑定为Circle类的对象,对象e动态绑定为Ellipse类的对象。这样,语句s.draw()调用的就是Shape类的方法draw(),语句c.draw()调用的就是Circle类的方法draw(),语句e.draw()调用的就是Ellipse类的方法draw()。
在程序中,存在动态多态的条件有三个:
<1>类和类之间有继承关系。
<2>有方法重写。
<3>存在父类引用子类对象。
因此在实际编程中,当父类定义对象时,可以动态绑定该类或该类子类的对象,则该对象的方法将随绑定对象不同而不同,即定义一个父类对象可以通过子类实例来调用子类的方法。
3.重载和重写的区别
重载和重写是面向对象设计的两个重要特征,它们有相似之处也有不同之处,具体区别如下。
<1>方法的重载是同一类中的方法之间的关系,是水平关系;方法的重写是子类和父类之间,是垂直关系。
<2>方法的重载是多个同名不同参的方法;方法的重写是对同一个方法产生关系,要求同名.同参和相同返回类型。
<3>方法重载对控制修饰符没有要求;方法的重写要求不能降低父类的访问控制权限。