多态通过分离做什么和怎么做,从另一角度将接口和实现分离开来,多态不但能够改善代码的组织结构和可读性,还能够创建可扩展的程序。
前期绑定在编译时期(如C),后期绑定是在运行时期(即多态,C++与Java),多态得程序的后期扩展。
Java中除了static方法和final方法(private方法属于final方法),其他所有的方法都是后期绑定。这意味着通常情况下,我们不必判定是否应该进行后期绑定——它会自动发生。
final方法会关闭动态绑定。
类的构造方法隐式的为static,它们实际上是static方法。
如果父类构造函数抛出异常,子类构造函数一定要抛出,不能被捕获。
覆写/重写父类方法时,子类方法异常处理有以下几种:
import java.io.IOException;
public class Parent {
public void overwrite(int i) throws IOException {}
public void overwrite() throws NullPointerException {}
}
import java.io.ObjectStreamException;
class SubSub extends Parent {
/*
* 父类抛出捕获型异常,子类却抛出运行时异常,这是可以,因为
* 抛出运行时就相当于没有抛出任何异常
*/
public void overwrite(int i) throws RuntimeException {}
/*
* 如果父类抛出的是非捕获型异常,则子类可以抛出任意非捕获型异
* 常,没有扩大异常范围这一问题
*/
public void overwrite() throws RuntimeException {}
}
class Sub2 extends Parent {
//也可以不抛出任何异常
public void overwrite(int i) {}
/*
* 如果父类抛出的是非捕获异常,子类也可以不用抛出,这与父类为捕
* 获型异常是一样的
*/
public void overwrite() {}
}
class Sub3 extends Parent {
//如果抛出的是捕获型异常,则只能是IOException的子类
//!!public void overwrite(int i) throws ObjectStreamException{}
/*
* 如果父类抛出的是非捕获异常,子类就不能抛出任何捕获型异常,因
* 为这样会扩大异常的范围
*/
//!! public void overwrite() throws IOException {}
}
//演员
class Actor {
public void act() {}
}
//继承(is-a关系):HappyActor(喜剧演员)确实是一种Actor(演员)
class HappyActor extends Actor {
public void act() { System.out.println("HappyActor"); }
}
//继承(is-a关系):HappyActor(悲剧演员)确实是一种Actor(演员)
class SadActor extends Actor {
public void act() { System.out.println("SadActor"); }
}
//舞台
class Stage {
private Actor actor = new HappyActor();//组合(has-a关系):舞台上有演员
public void change() { actor = new SadActor(); }
public void performPlay() { actor.act(); }
}
public class Transmogrify {
public static void main(String[] args) {
Stage stage = new Stage();
stage.performPlay();
stage.change();//运行过程中改变具体性为
stage.performPlay();
}
}
/* Output:
HappyActor
SadActor
*///:~
public class PrivateOverride {
private void f() { System.out.println("private f()"); }
public static void main(String[] args) {
PrivateOverride po = new Derived();
po.f();
}
}
class Derived extends PrivateOverride {
public void f() { System.out.println("public f()"); }
} /* Output:
private f()
*///:~
class Super {
public int field = 0;
public int getField() {
return field;
}
}
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(); // Upcast
System.out.println("sup.field = " + sup.field + ", sup.getField() = "
+ sup.getField());
Sub sub = new Sub();
System.out.println("sub.field = " + sub.field + ", sub.getField() = "
+ sub.getField() + ", sub.getSuperField() = "
+ sub.getSuperField());
}
} /*
* Output:
* sup.field = 0, sup.getField() = 1
* sub.field = 1, sub.getField() = 1, sub.getSuperField() = 0
*/// :~
class StaticSuper {
public static String staticGet() {
return "Base staticGet()";
}
public String dynamicGet() {
return "Base dynamicGet()";
}
}
class StaticSub extends StaticSuper {
public static String staticGet() {
return "Derived staticGet()";
}
public String dynamicGet() {
return "Derived dynamicGet()";
}
}
public class StaticPolymorphism {
public static void main(String[] args) {
StaticSuper sup = new StaticSub(); // Upcast
System.out.println(sup.staticGet());
System.out.println(sup.dynamicGet());
}
} /* Output:
Base staticGet()
Derived dynamicGet()
*///:~
如果自己处理清理动作,则销毁的顺序应该和初始化顺序相反。对于字段,则意味着与声明的顺序相反(因为字段的初始化是按照声明的顺序进行的)。对于基类,应该首先对其子类进行清理,然后才是基类,这是因为子类的清理可能会调用基类中的某些方法,所以需要使用基类中的构件仍起作用而不应过早地销毁它们,这与C++中的析构函数形式是一样的。
在父类的构造函数中调用多态方法时,可能会引发问题:
class Glyph {
void draw() {
System.out.println("Glyph.draw()");
}
Glyph() {
System.out.println("Glyph() before draw()");
draw();//在父类构造函数中调用多态方法
System.out.println("Glyph() after draw()");
}
}
class RoundGlyph extends Glyph {
private int radius = 1;
RoundGlyph(int r) {
radius = r;
System.out.println("RoundGlyph.RoundGlyph(), radius = " + radius);
}
void draw() {
System.out.println("RoundGlyph.draw(), radius = " + radius);
}
}
public class PolyConstructors {
public static void main(String[] args) {
new RoundGlyph(5);
}
} /* Output:
Glyph() before draw()
RoundGlyph.draw(), radius = 0
Glyph() after draw()
RoundGlyph.RoundGlyph(), radius = 5
*///:~
在构造函数内唯一能够安全调用的那些方法是基类中的final方法(当然也适用于private方法,它们自动属于final方法)。
子类覆盖父类方法时,子类的返回类型可以是父类返回类型的某个子类型,但返过来不行。
class Grain {}
class Wheat extends Grain {}
class Mill {
Grain p() { return new Grain(); }
Wheat f() { return new Wheat(); }
float g() { return 0;}
}
class WheatMill extends Mill {
Wheat p() { return new Wheat(); }
//子类覆盖父类方法时返回类型不能比子类宽
//!! Grain f() { return new Grain(); }
//虽然int可以隐式的转换成float,但int不是float的子类,所以编译不能通过
//!! int g() { return 0;}
}