小A:“什么是构造器?”
大B:“首先要注意的是Java的构造器并不是函数,所以他并不能被继承,这在我们extends的时候写子类的构造器时比较的常见,即使子类构造器参数和父类的完全一样,我们也要写super就是因为这个原因。构造器的修饰符比较的有限,仅仅只有publicprivateprotected这三个,其他的例如任何修饰符都不能对其使用,也就是说构造器不允许被成名成抽象、同步、静态等等访问限制以外的形式。因为构造器不是函数,所以它是没有返回值的,也不允许有返回值。但是这里要说明一下,构造器中允许存在return语句,但是return什么都不返回,如果你指定了返回值,虽然编译器不会报出任何错误,但是JVM会认为他是一个与构造器同名的函数罢了,这样就会出现一些莫名其妙的无法找到构造器的错误,这里是要加倍注意的。”
小A:“在我们extends一个子类的时候经常会出现一些意想不到的问题,你能和我说说一些和构造器有关的吗?”
大B:“首先说一下Java在构造实例时的顺序(不讨论装载类的过程),构造的粗略过程如下1、分配对象空间,并将对象中成员初始化为0或者空,java不允许用户操纵一个不定值的对象。2、执行属性值的显式初始化。3、执行构造器。4、将变量关联到堆中的对象上。”
小A:“能介绍一下准备知识吗?以备一会来详细了解这个的流程。”
大B:“this()super()是你如果想用传入当前构造器中的参数或者构造器中的数据调用其他构造器或者控制父类构造器时使用的,在一个构造器中你只能使用this()或者super()之中的一个,而且调用的位置只能在构造器的第一行,在子类中如果你希望调用父类的构造器来初始化父类的部分,那就用合适的参数来调用super(),如果你用没有参数的super()来调用父类的构造器(同时也没有使用this()来调用其他构造器),父类缺省的构造器会被调用,如果父类没有缺省的构造器,那编译器就会报一个错误,注意这里,我们经常在继承父类的时候构造器中并不写和父类有关的内容,此时如果父类没有缺省构造器,就会出现编译器添加的缺省构造器给你添麻烦的问题了哦!例如:Classbextendsa{publicb(){}}就没有任何有关父类构造器的信息,这时父类的缺省构造器就会被调用。”
举个SL-275中的例子
publicclassManagerextendsEmployee{
privateStringdepartment;
publicManager(Stringname,doublesalary,Stringdept)
{
super(name,salary);
department=dept;
}
publicManager(Stringn,Stringdept){
super(name);
department=dept;
}
publicManager(Stringdept){//这里就没有super(),编译器会自动地添加一个空参数的缺省super构造器,此时如果Employee类中没有空参数的缺省构造器,那就会导致一个编译错误。
department=d;
}
}
大B:“你必须在构造器的第一行放置super或者this构造器,否则编译器会自动地放一个空参数的super构造器的,其他的构造器也可以调用super或者this,调用成一个递归构造链,最后的结果是父类的构造器(可能有多级父类构造器)始终在子类的构造器之前执行,递归的调用父类构造器。在具体构造类实例的过程中,上边过程的第二步和第三步是有一些变化的,这里的顺序是这样的,分配了对象空间及对象成员初始化为默认值之后,构造器就递归的从继承树由根部向下调用,每个构造器的执行过程是这样的:1、Bind构造器的参数。2、如果显式的调用了this,那就递归调用this构造器然后跳到步骤5.3、递归调用显式或者隐式的父类构造器,除了Object以外,因为它没有父类。4、执行显式的实例变量初始化(也就是上边的流程中的第二步,调用返回以后执行,这个步骤相当于在父构造器执行后隐含执行的,看样子像一个特殊处理)。5、执行构造器的其它部分。”
小A:“好像有点明白了。”
大B:“这里的步骤很重要哦!从这个步骤中可以很明显的发现这个实例初始化时的递归调用过程。”