构造器和多态
构造器的调用顺序
首先,我们知道构造器是为了能够给对象正确的进行初始化赋值使用的,为了能够生成一个正确的对象;如果只是一个类,那么构造器在创建对象的时候调用,不会出现什么匪夷所思的事情,但是如果有了相关的继承关系
基类的构造器总是在导出类的构造过程中被调用,而且按照继承层次逐渐向上链接,以使每个基类构造器都能够得到调用,这样做是非常有意义的,因为导出类只能访问自己的成员,不能访问基类中private类型的成员。所以要保证基类中的属性表现出正确的行为,必须由他的构造器对他们进行初始化操作。
package duotai;
public class Meal {
Meal(){
System.out.println("Meal()");
}
}
//----------------------------------------
package duotai;
public class Bread {
Bread(){
System.out.println("Bread()");
}
}
//----------------------------------------
package duotai;
public class Cheese {
Cheese(){
System.out.println("Cheese()");
}
}
//----------------------------------------
package duotai;
public class Lettuce {
Lettuce(){
System.out.println("Lettuce()");
}
}
//----------------------------------------
package duotai;
public class Lunch extends Meal{
Lunch(){
System.out.println("Lunch()");
}
}
//----------------------------------------
package duotai;
public class PortableLunch extends Lunch {
PortableLunch(){
System.out.println("PortableLunch()");
}
}
//----------------------------------------
package duotai;
public class SandWich extends PortableLunch {
private Bread b = new Bread();
private Cheese c = new Cheese();
private Lettuce l = new Lettuce();
public SandWich() {
System.out.println("Sandwich()");
}
public static void main(String[] args) {
new SandWich();
}
}
//-------------------------------------------
//output:
Meal()
Lunch()
PortableLunch()
Bread()
Cheese()
Lettuce()
Sandwich()
在final关键字那篇文章里我大概说过了这个过程
final关键字
1、不断反复递归调用基类构造器
2、按声明顺序调用成员的初始化方法
3、调用导出类构造器的主体
//先各个属性,然后是构造器方法中的初始化赋值
构造器内部的多态方法的行为
class Glyph{
void draw(){ print("Glyph.draw()") }
Glyph(){
print("Glyph() before draw()");
draw();
print("Glyph() after draw()");
}
}
//-------------------------------------------
class RoundGlyph extends Glyph{
private int radius = 1;
RoundGlyph(int r){
radius = r;
print("RoundGlyph.RoundGlyph().radius = " + radius);
}
void draw(){
print("RoundGlyph.draw().radius = " + radius);
}
}
//-----------------------------------------
public class PolyConstructors{
public static void main(String[] args){
new ToundGlyph(5);
}
}
//output:
Glyph() before draw()
RoundGlyph.draw().radius = 0
Glyph() after draw()
RoundGlyph.RoundGlyph().radius = 5
神奇的事情出现了,第二行打印出来了一个“0”
首先他会递归,调用了Glyph的构造方法,然后在调用构造方法中的draw()的时候,调用了他的重写方法
更详细一点,初始化的实际过程是:
1、将分配给对象的存储空间赋零值
2、调用基类构造器,此时,调用被覆盖后的draw()方法,这个过程是在调用RoundGlyph构造器之前调用的,由于第一步,所以这时候radius的值是0。
3、按照声明的顺序调用成员的初始化方法
4、调用导出类的构造函数主体