一、什么是多态,什么是动态绑定
在面向对象程序设计中,多态是继数据抽象和继承之后的第三种基本特征
多态通过做什么和怎么做,从另一角度将接口和实现分开。多态不但能够改善代码的组织结构和可读性,还能够创建可扩展的程序。
多态也称作动态绑定、后期绑定或运行时绑定
下面,我们通过一个例子来具体示范一下所谓的多态:
public class Shape {
public void draw(){
System.out.println("Shape draw");
}
}
public class Cicle extends Shape{
@Override
public void draw(){
System.out.println("Cicle draw");
}
}
public class Triangle extends Shape {
@Override
public void draw(){
System.out.println("Triangle draw");
}
}
public class Test {
public static void main(String[] args) {
Shape c = new Cicle();
Shape t = new Triangle();
c.draw();
t.draw();
}
}
运行结果:
Cicle draw
Triangle draw
这里,创建了一个Cicle
对象和一个Triangle
对象,并分别把得到的引用赋值给两个Shape
。这两个引用调用了draw()
方法。根据运行结果可以看出明,两个Shape
对象的引用,并没有调用Shape
对象的draw()
方法,而是调用了其指向对象的draw()
方法。这就是动态绑定,编译器在运行时根据对象的类型进行绑定。
Java中除了static方法和final方法(private方法属于final方法)之外,其他所有的方法都是动态绑定。这意味着通常情况下,我们不必判定是否应该进行后期绑定——它会自动发生。
二、为什么要动态绑定
1、产生正确的行为
一旦知道了Java中所有方法都是通过动态绑定实现的多态这个事实,我们就可以编写只与基类打交道的程序代码了,并且这些代码对所有的导出类都可以正确运行。或者换一种说法,发送消息给某个对象,让该对象判定应该做什么事。
2、可扩展性
三、多态的缺陷
1、“覆盖私有方法”
public class PrivateOverrite {
private void f() {
System.out.println("private f()");
}
public static void main(String[] args) {
PrivateOverrite po = new Derived();
po.f();
}
}
public class Derived extends PrivateOverrite {
public void f() {
System.out.println("public f()");
}
}
输出结果:
private f()
在导出类中,我们添加了一个与基类中一个私有方法同名的方法,再用基类引用指向导出类对象。用引用调用f()
方法,本期望输出public f()
,实际上,却调用了基类的似有方法,输出了private f()
。
结论就是:只有非
private
方法才可以被覆盖;但是还需密切注意private
方法的现象,这时虽然编译器不会报错,但是也不会按照我们所期望的来执行。确切地说,在导出类中,对与基类中的private
方法,最好采用不同的名字。
2、域与静态方法
public class Super {
public int field = 0;
public int getField() {
return field;
}
}
public class Sub extends Super {
public int field = 1;
public int getField() {
return field;
}
public int getSuperField() {
return super.field;
}
}
public class FieldAccess {
public static void main(String[] args) {
Super sup = new Sub();
System.out.println("sup.field = " + sup.field);
System.out.println("sup.getField() = " + sup.getField());
Sub sub = new Sub();
System.out.println("sub.field = " + sub.field);
System.out.println("sub.getField() = " + sub.getField());
System.out.println("sub.getSuperField() = " + sub.getSuperField());
}
}
运行结果:
sup.field = 0
sup.getField() = 1
sub.field = 1
sub.getField() = 1
sub.getSuperField() = 0
咦,结果似乎有点不对劲,导出类引用指向基类对象再调用属性为什么会得到基类属性?答案就是:类中并非任何事物都可以多态地发生。
当Sub对象转型为Super引用时,任何域访问操作都将交由编译器解析,因此不是多态的。