一个例子:
public enum Note {
MIDDLE_C, C_SHARP, B_FLAT; // Etc.
}
class Instrument {
public void play(Note n) {
System.out.println("Instrument.play()");
}
}
public class Wind extends Instrument {
// Redefine interface method:
@Override public void play(Note n) {
System.out.println("Wind.play() " + n);
}
}
Wind 是一种 Instrument;因此,Wind 继承 Instrument。
public class Music {
public static void tune(Instrument i) {
i.play(Note.MIDDLE_C);
}
public static void main(String[] args) {
Wind flute = new Wind();
tune(flute); // Upcasting
}
}
/* Output:
Wind.play() MIDDLE_C
*/
tune方法传入了一个Wind类型的引用,tune方法参数的类型的Instrument类型,这种情况属于向上转型,因此不需要做类型转化
如果没有多态的概念,那么当有多种乐器时,我们就需要为每种乐器都编写一个tune方法,非常繁琐,由于多态机制,你可以向系统中添加任意多的新类型,而不需要修改 tune() 方法。
class Stringed extends Instrument {
@Override public void play(Note n) {
System.out.println("Stringed.play() " + n);
}
}
class Brass extends Instrument {
@Override public void play(Note n) {
System.out.println("Brass.play() " + n);
}
}
public class Music2 {
public static void tune(Wind i) {
i.play(Note.MIDDLE_C);
}
public static void tune(Stringed i) {
i.play(Note.MIDDLE_C);
}
public static void tune(Brass i) {
i.play(Note.MIDDLE_C);
}
public static void main(String[] args) {
Wind flute = new Wind();
Stringed violin = new Stringed();
Brass frenchHorn = new Brass();
tune(flute); // No upcasting
tune(violin);
tune(frenchHorn);
}
}
/* Output:
Wind.play() MIDDLE_C
Stringed.play() MIDDLE_C
Brass.play() MIDDLE_C
*/
一个问题: tune方法中的形参类型是Instrument类型, 传入Wind引用时调用的为什么是Wind类中重写的方法?
方法调用绑定:
前期绑定存在的问题: 以上面的代码为例,编译器只知道Instrument引用类型,具体调用的是Wind的tune方法还是Brass的tune方法,这就不得而知了。
后期绑定:在运行时根据对象的类型进行绑定。后期绑定也称为动态绑定或运行时绑定
Java 中除了 static 和 final 方法(fianl方法不能被子类重写)(private 方法也是隐式的 final)外,其他所有方法都是后期绑定
私有方法可以被子类重写吗?:
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()
*/
期望输出是 public f(),然而 private 方法可以当作是 final 的,对于派生类来说是隐蔽的。因此,这里 Derived 的 f() 是一个全新的方法,最终调用的是基类的f()
非 private 方法才能被重写,但是得小心重写 private 方法的现象,编译器不报错
属性与静态方法:
Father f=new Child(); System.out.println(f.name); 此时打印的是父类中的name值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()";
}
@Override public String dynamicGet() {
return "Derived dynamicGet()";
}
}
public class StaticPolymorphism {
public static void main(String[] args) {
StaticSuper sup = new StaticSub(); // Upcast
System.out.println(StaticSuper.staticGet());
System.out.println(sup.dynamicGet());
}
}
/* Output:
Base staticGet()
Derived dynamicGet()
*/
构造器内部多态方法的行为:
在父类的构造器内部调用子类重写的方法
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);
}
@Override 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
*/
调用的是子类的draw方法符合预期,但是打印出的radius的值是0而不是1,这一点不符合预期,这一点的原因在于初始化加载的以下顺序:
所以在父类的构造器中调用子类的draw方法时,子类的radius值还没进行显示赋值,因此使用的是默认值