其实这将是"《JAVA编程思想》第四版学习 需要我记住的something -复用类"的一部分,之所以单独提出来,并提前发出来,只是要增强自己的记忆吧。 Java自动在派生类构造器中插入调用基类构造器的代码,即使未定义派生类构造器,编译器仍会产生一个默认构造器来调用基类构造器,而且基类构造器调用发生在派生类构造器之前。
那么,是不是可以认为基类构造器调用是派生类构造器的第一条语句,从而在派生类构造器调用的时候才调用呢?不是。请看这个代码:class A {
A() { System.out.println("A constructor"); }
}
class B {
B() { System.out.println("B constructor"); }
}
public class C extends A {
B b = new B();
public static void main(String[] args) {
C c = new C();
}
}
按照初始化顺序,那么定义时的初始化发生在构造器之前,也就是b的赋值应该发生在C的默认构造器之前,如果基类构造器调用是派生类构造器的第一条语句,从而在派生类构造器调用的时候才调用,那运行结果应该是
B constructor
A constructor
而实际结果是
A constructor
B constructor
显然,"基类构造器调用是派生类构造器的第一条语句,从而在派生类构造器调用的时候才调用"这个说法是错误的。应该是:基类的初始化发生在派生类的初始化(包括自动初始化、构造器等等)之前。 前面说的是无参构造器(默认构造器),对于有参数的构造器:
1. 如果基类构造器都有参数,必须显式调用基类构造器(super(...))(如果不,编译器报错,找不到基类默认构造器),而且必须是派生类构造器的第一条语句(如果不,编译器报错,找不到基类默认构造器和super调用必须是第一条语句两个错误)。
2. 如果基类构造器没有参数,则不需显式调用,编译器会为你完成。
3. 如果派生类有多个构造器,在基类构造器有参数的时候,你显然得在所有派生类构造器中加入super(...);如果基类构造器没有参数,编译器会在所有派生类构造器内加入对基类构造器的调用(因为编译器不知道你会调用哪个构造器,对吧?嘿嘿)。看例子class A {
A() { System.out.println("A constructor"); }
}
class B {
B(int i) { System.out.println("B constructor"); }
}
public class C7 extends A {
B b = new B(1);
public C7(int i)
{
b = new B(i);
System.out.println("C7(int i) constructor");
}
public C7()
{
System.out.println("C7() constructor");
}
public static void main(String[] args) {
C7 c = new C7(10);
c = new C7();
}
}
结果:
A constructor
B constructor
B constructor
C(int i) constructor
A constructor
B constructor
C() constructor 在显式调用的情况下,似乎上面的讨论又是错的,基类构造器调用就是派生类构造器的第一条语句,在派生类构造器调用的时候才调用。结果呢?再次证明这句话是错的。你仍然得说:基类的初始化发生在派生类的初始化(包括自动初始化、构造器等等)之前。看这个例子:class A {
A(int i) { System.out.println("A constructor"); }
}
class B {
B(int i) { System.out.println("B constructor"); }
}
public class C7 extends A {
B b = new B(1);
public C7(int i)
{
super(i);
b = new B(i);
System.out.println("C7 constructor");
}
public static void main(String[] args) {
C7 c = new C7(10);
}
} 结果:
A constructor
B constructor
B constructor
C constructor
结果说明了一切,虽然你super(i)是在派生类构造器的第一条语句。 总之一句话,未显式调用基类构造器的情况下,编译器就查找基类的默认构造器,找不到报错;找到,加入对它的调用。基类没有默认构造器,就必须显式调用。基类的初始化发生在派生类的初始化(包括自动初始化、构造器等等)之前。更多内容,请参见我的blog http://blog.csdn.net/icesnows
那么,是不是可以认为基类构造器调用是派生类构造器的第一条语句,从而在派生类构造器调用的时候才调用呢?不是。请看这个代码:class A {
A() { System.out.println("A constructor"); }
}
class B {
B() { System.out.println("B constructor"); }
}
public class C extends A {
B b = new B();
public static void main(String[] args) {
C c = new C();
}
}
按照初始化顺序,那么定义时的初始化发生在构造器之前,也就是b的赋值应该发生在C的默认构造器之前,如果基类构造器调用是派生类构造器的第一条语句,从而在派生类构造器调用的时候才调用,那运行结果应该是
B constructor
A constructor
而实际结果是
A constructor
B constructor
显然,"基类构造器调用是派生类构造器的第一条语句,从而在派生类构造器调用的时候才调用"这个说法是错误的。应该是:基类的初始化发生在派生类的初始化(包括自动初始化、构造器等等)之前。 前面说的是无参构造器(默认构造器),对于有参数的构造器:
1. 如果基类构造器都有参数,必须显式调用基类构造器(super(...))(如果不,编译器报错,找不到基类默认构造器),而且必须是派生类构造器的第一条语句(如果不,编译器报错,找不到基类默认构造器和super调用必须是第一条语句两个错误)。
2. 如果基类构造器没有参数,则不需显式调用,编译器会为你完成。
3. 如果派生类有多个构造器,在基类构造器有参数的时候,你显然得在所有派生类构造器中加入super(...);如果基类构造器没有参数,编译器会在所有派生类构造器内加入对基类构造器的调用(因为编译器不知道你会调用哪个构造器,对吧?嘿嘿)。看例子class A {
A() { System.out.println("A constructor"); }
}
class B {
B(int i) { System.out.println("B constructor"); }
}
public class C7 extends A {
B b = new B(1);
public C7(int i)
{
b = new B(i);
System.out.println("C7(int i) constructor");
}
public C7()
{
System.out.println("C7() constructor");
}
public static void main(String[] args) {
C7 c = new C7(10);
c = new C7();
}
}
结果:
A constructor
B constructor
B constructor
C(int i) constructor
A constructor
B constructor
C() constructor 在显式调用的情况下,似乎上面的讨论又是错的,基类构造器调用就是派生类构造器的第一条语句,在派生类构造器调用的时候才调用。结果呢?再次证明这句话是错的。你仍然得说:基类的初始化发生在派生类的初始化(包括自动初始化、构造器等等)之前。看这个例子:class A {
A(int i) { System.out.println("A constructor"); }
}
class B {
B(int i) { System.out.println("B constructor"); }
}
public class C7 extends A {
B b = new B(1);
public C7(int i)
{
super(i);
b = new B(i);
System.out.println("C7 constructor");
}
public static void main(String[] args) {
C7 c = new C7(10);
}
} 结果:
A constructor
B constructor
B constructor
C constructor
结果说明了一切,虽然你super(i)是在派生类构造器的第一条语句。 总之一句话,未显式调用基类构造器的情况下,编译器就查找基类的默认构造器,找不到报错;找到,加入对它的调用。基类没有默认构造器,就必须显式调用。基类的初始化发生在派生类的初始化(包括自动初始化、构造器等等)之前。更多内容,请参见我的blog http://blog.csdn.net/icesnows
而且你所以认为结果出乎预期的猜测都是以“程序应该按照从上到下的顺序初始化”这个假设为前提,但是原文里也并没有这么说,而且真个Thinking里也没这么说过,好像从C++开始就已经不是按照顺序初始化了。
可能是LZ由于对某些名次或者概念的理解造成这种印象的
一. 我所说的初始化!=调用构造器。初始化有automation initiatlization、specifying initiatlization、explicit static initialization(static clause)、instance initialization(instance clause)、constructor,etc.二. 我从来没有说“程序应该按照从上到下的顺序初始化”。我只是想提醒自己这个初学者,即使你在派生类构造器写下super()这条语句,不代表基类的初始化会发生在派生类之后,因为按照我们一般的理解,一个方法里的代码是顺序执行的,不是么?
当然这里面有个例外,java.lang.Object类的对象初始化方法是不会调用另外初始化方法的,因为它没有父类了。
是这样么?那第一个例子怎么解释?C的new B()是不是应该在C的构造器之前调用?为什么B的构造器在A之后调用?
============================================================================================
我想这里是有问题吧,定义时的b初始化是C默认构造器的有机组成部分之一,是位于顺序前列
严格来说,构造方法里面所包含的执行语句只是对象初始化方法中执行语句的一部分,对象初始化方法的执行语句包含了成员变量直接初始化语句、对象初始化块以及构造方法内执行语句。而对象初始化方法的第一句调用的就是父类对象初始化方法或者本类另外一个对象初始化方法、接着才是成员变量初始化语句,然后再对象初始化块语句,构造方法执行语句。这就可以解释为什么new B()的语句会在调用A的构造方法之后的原因了。
2:有参和无参都会调用super对应的构造函数的。
我只是个新手,刚学Java没多久,以前看过C++,不过都忘了
最近在看thinking in java
觉得应该把自己概念不是很清晰的,或者是新学到的都记录下来
所以就写出来了
的构造器,又是什么时候执行自身的。
1)是不是可以认为基类构造器调用是派生类构造器的第一条语句,从而在派生类构造器调用的时候才调用呢?不是
2)显然,"基类构造器调用是派生类构造器的第一条语句,从而在派生类构造器调用的时候才调用"这个说法是错误的
3)基类构造器调用就是派生类构造器的第一条语句,在派生类构造器调用的时候才调用。结果呢?再次证明这句话是错的。我明确对楼主说:你错了。
这句话在不考虑“局部内部类访问final型局部变量时”这个条件下,是完全正确的。
所以说:只靠一些自己摸索例子,就归纳总结结论,而不去深究“编译程序内部到底是如何安排编译代码的”,才会导致楼主在云里雾里绕来绕去,变得很复杂。其实这个正说明:楼主自己似乎好象明白了,其实没有真正的明白。
静态初始化块
static
{
//...
}
实例初始化块
{
//...
}