1.绑定
定义:将一个方法调用同一个方法主体关联起来称作绑定。
前期绑定 就是在程序运行之间就知道调用哪个主体的方法。
例子:Shape是基类
public class Shape {
public void draw() {}
public void erase() {}
}
public class Circle extends Shape {
public void draw() { print("Circle.draw()"); }
public void erase() { print("Circle.erase()"); }
}
public class Triangle extends Shape {
public void draw() { print("Triangle.draw()"); }
public void erase() { print("Triangle.erase()"); }
}
---------
public void applyDraw(Shape shape){
shape.draw();
}
applyDraw(new Circle());
此时shape.draw()在程序运行前不知道用哪个子类的draw。所以用后期绑定,后期绑定也是有某种类型信息在对象里面。
java中除了static和final(private方法也是final方法)其他方法都是后期绑定。static是静态,不和具体的对象实例联系。final是不想让其他覆盖,这是它第一个目的,还有就是在编译时直接插入到调用的位置,问题就是方法体长的效率反而低,所以使用final主要以第一个为选用标准。
2.覆盖私有方法的假象
public class PrivateOverride {
private void f() { print("private f()"); }
public static void main(String[] args) {
PrivateOverride po = new Derived();
po.f();
}
}
class Derived extends PrivateOverride {
public void f() { print("public f()"); }
} /* Output:
private f()
*///:~
因为private没有被覆盖,所以向上转型后使用的父类自己的f();
3.域和静态方法
public int field=0;
public static String method(){}
这两个在向上转型后都是使用父类的域和静态方法。 在此所有对域的访问通过编译器,也就是说在编译期间就能确定。静态方法只是与类连在一起,所以也是编译期间就可以确定.子类可以具有父类的可见(覆盖后)方法和域,总之,域和静态方法不具多态性。
只有普通方法的调用是多态的。
4.构造器和多态
构造器其实是隐式的static,不具备多态性,但是构造器的调用顺序还是要注意一下。
class Glyph {
void draw() { print("Glyph.draw()"); }
Glyph() {
1. print("Glyph() before draw()");
draw();
3. print("Glyph() after draw()");
}
}
class RoundGlyph extends Glyph {
private int radius = 1;
RoundGlyph(int r) {
radius = r;
4. print("RoundGlyph.RoundGlyph(), radius = " + radius);
}
void draw() {
2. print("RoundGlyph.draw(), radius = " + radius); 此时成员初始化还没执行,所以radius是0.
}
}
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
*///:~
构造顺序
1.任何事物发生之前,分配给对象的存储空间初始化成二进制的零。这个第二步radius为0的原因。
2.基类成员then基类构造器。
3.重复2。
4.导出类的成员then构造器。
5.协变返回类型
直接说它的含义有点绕,下面通过例子说明一下
class Mill {
Grain process() { return new Grain(); }
}
class WheatMill extends Mill {
Wheat process() { return new Wheat(); }
}
Wheat是Grain的一个导出类。
注意此时process的返回类型不一样。
Mill m= new WheatMill();
m.process返回的是Wheat。这就是协变返回,导出类WheatMill中覆盖的方法process可以返回基类方法process返回类型Grain的某种导出类型Wheat。
核心:这里process是覆盖的,因为返回类型Grain是Wheat的基类,这个条件很重要。
6.纯继承和扩展
老问题了,纯继承是is-a的关系,扩展时is-like-a的关系。
7.向下转型成某个导出类都要进行类型检查 。