1. 方法调用绑定
将一个方法调用同一个方法主体关联起来被称作绑定。若在程序执行前进行绑定(如果有的话,由编译器和连接程序实现),叫做前期绑定。
在运行时根据对象的类型进行绑定叫做后期绑定,也叫做动态绑定或运行时绑定。如果一种语言想实现后期绑定,就必须具有某种机制,以便在运行时能判断对象的类型,从而调用恰当的方法。
Java中除了static方法和final方法(private方法属于final方法)之外,其他所有的方法都是后期绑定。这意味着通常情况下,我们不必判定是否应该进行后期绑定——它会自动发生。
2. 缺陷:“覆盖”私有方法
只有非private方法才可以被覆盖;但是还需要密切注意覆盖private方法的现象,这时虽然编译器不会报错,但是也不会按照我们所期望的来执行。确切的说,在导出类中,对于基类中的private方法,最好采用不同的名字。
3. 域与静态方法
然而,只有普通的方法调用可是是多态的。例如,如果你直接访问某个域,这个访问就将在编译期进行解析。在向上转型时,任何域访问操作都将由编译器解析,因此不是多态的。还有类似的就是,如果某个方法是静态的,它的行为就不具有多态性。
4. 基类的构造器总是在导出类的构造过程中被调用,而且按照继承层次逐渐向上链接,以使每个基类的构造器都能得到调用。这样做是有意义的,因为构造器具有一项特殊任务:检查对象是否被正确的构造。导出类只能访问它自己的成员,不能访问基类中的成员(基类成员通常是private类型)。只有基类的构造器才具有恰当的知识和权限来对自己的元素进行初始化。因此,必须令所有构造器都得到调用,否则就不可能正确构造完整对象。这正是编译器为什么要强制每个导出类都必须调用构造器的原因。在导出类的构造器主体中,如果没有明确指定调用某个构造器,它就会“默默”地调用默认构造器。如果不存在默认构造器,编译器就会报错。
5. 所以万一某个子对象要依赖于其他对象,销毁的顺序应该和初始化顺序相反。对于字段,则意味着与生命的顺序相反(因为字段的初始化是按照声明的顺序进行的)。对于基类,应该首先对其导出类进行清理,然后才是基类。这是因为导出类的清理可能会调用基类中的某些方法,所以需要使基类中的构件仍起作用而不应过早的销毁它们。
6. 初始化的实际过程是:
(1). 在其他任何事物发生之前,将分配给对象的存储空间初始化成二进制的零。
(2). 如前所述那样调用基类的构造器。此时,调用被覆盖后的方法。
(3). 按照声明的顺序调用成员的初始化方法。
(4). 调用导出类的构造器主体。
编写构造器时有一条有效的准则:“用尽可能简单的方法使对象进入正常状态;如果可以的话,避免调其他方法”。在构造器内唯一能够安全调用的那些方法是基类中的final方法(也适用于private方法,它们自动属于final方法)。
7. 协变返回类型
public class CovariantReturn {
public static void main(String[] args) {
Mill m = new Mill();
Grain g = m.process();
System.out.println(g);
m = new WheatMill();
g = m.process();
System.out.println(g);
}
}
class Grain {
public String toString() {
return "Grain";
}
}
class Wheat extends Grain {
public String toString() {
return "Wheat";
}
}
class Mill {
Grain process() {
return new Grain();
}
}
class WheatMill extends Mill {
Wheat process() {
return new Wheat();
}
}
输出结果:
Grain
Wheat
Java SE5中添加了协变返回类型,它表示在导出类中的被覆盖方法可以返回基类方法的返回类型的某种导出类型。
8. 多态意味着“不同的形式”。在面向对象的程序设计中,我们持有从基类继承而来的相同接口,以及使用该接口的不同形式:不同版本的动态绑定。堕胎是一种不能单独来看待的特性(例如,像switch语句是可以的),相反它只能作为类关系“全景”中的一部分,与其他特性协同工作。
9. 覆盖方法时应注意的事项:
若要覆盖父类的方法,返回参数可以是父类返回参数的子类。
子类对父类方法覆盖时,方法的访问权限大于等于父类的权限。