其实这将是"《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

解决方案 »

  1.   

    没太看明白你说的这个是什么意思,两个有什么不同吗?你所说的初始化,不就是原文所说的调用构造器吗!
    而且你所以认为结果出乎预期的猜测都是以“程序应该按照从上到下的顺序初始化”这个假设为前提,但是原文里也并没有这么说,而且真个Thinking里也没这么说过,好像从C++开始就已经不是按照顺序初始化了。
    可能是LZ由于对某些名次或者概念的理解造成这种印象的
      

  2.   

    我想说明两点:
    一. 我所说的初始化!=调用构造器。初始化有automation initiatlization、specifying initiatlization、explicit static initialization(static clause)、instance initialization(instance clause)、constructor,etc.二. 我从来没有说“程序应该按照从上到下的顺序初始化”。我只是想提醒自己这个初学者,即使你在派生类构造器写下super()这条语句,不代表基类的初始化会发生在派生类之后,因为按照我们一般的理解,一个方法里的代码是顺序执行的,不是么?
      

  3.   

    新建一java对象,它的内部初始化顺序为: 1.  父类静态成员和静态初始化块 ,按在代码中出现的顺序依次执行 2.  子类静态成员和静态初始化块 ,按在代码中出现的顺序依次执行 3.  父类实例成员和实例初始化块 ,按在代码中出现的顺序依次执行 4.  父类构造方法 5.  子类实例成员和实例初始化块 ,按在代码中出现的顺序依次执行 6.  子类构造方法
      

  4.   

    本身即是如此,子类对象初始化方法第一句调用父类对象初始化方法,而不是什么父类对象初始化方法会在子类对象初始化方法之前运行,先运行的还是子类对象初始化方法,而子类对象初始化方法的第一句一定是调用另外一个初始化方法,任何类都是这样,但是初始化方法的第一句不一定就是调用父类初始化方法,也可能调用本类另外一个初始化方法,例如this(...);语句在构造方法开头的情况下。
    当然这里面有个例外,java.lang.Object类的对象初始化方法是不会调用另外初始化方法的,因为它没有父类了。
      

  5.   


    是这样么?那第一个例子怎么解释?C的new B()是不是应该在C的构造器之前调用?为什么B的构造器在A之后调用?
      

  6.   

    按照初始化顺序,那么定义时的初始化发生在构造器之前,也就是b的赋值应该发生在C的默认构造器之前
    ============================================================================================
    我想这里是有问题吧,定义时的b初始化是C默认构造器的有机组成部分之一,是位于顺序前列
      

  7.   

    请注意我的用词,我所说的是"对象初始化方法"而不是所谓的"构造方法",对象初始化方法是存在于编译后的class文件中的,而构造方法是存在于java源代码文件中的,虽然初始化方法与构造方法一一对应,但是对象初始化方法与构造方法还是不同的,很多时候很多书上把构造方法等同于对象初始化方法,这也不能说他是错误的,但是这里谈论到这个问题,我希望把它们分开,毕竟class文件中的对象初始化方法与java源代码的构造方法不同。
    严格来说,构造方法里面所包含的执行语句只是对象初始化方法中执行语句的一部分,对象初始化方法的执行语句包含了成员变量直接初始化语句、对象初始化块以及构造方法内执行语句。而对象初始化方法的第一句调用的就是父类对象初始化方法或者本类另外一个对象初始化方法、接着才是成员变量初始化语句,然后再对象初始化块语句,构造方法执行语句。这就可以解释为什么new B()的语句会在调用A的构造方法之后的原因了。
      

  8.   

    我个人认为 B b = new B();1: 这个初始化是用构造器初始化的,只不过在构造器最前面罢了
    2:有参和无参都会调用super对应的构造函数的。
      

  9.   


    我只是个新手,刚学Java没多久,以前看过C++,不过都忘了
    最近在看thinking in java
    觉得应该把自己概念不是很清晰的,或者是新学到的都记录下来
    所以就写出来了
      

  10.   

    我也有个困惑,想就贴问一下,既然Java会自动在子类构造器插入对基类构造器的调用,想知道什么时候执行基类
    的构造器,又是什么时候执行自身的。
      

  11.   

    答:楼主的钻研精神,很令人高兴。但楼主搞了大半天,想证明:
    1)是不是可以认为基类构造器调用是派生类构造器的第一条语句,从而在派生类构造器调用的时候才调用呢?不是
    2)显然,"基类构造器调用是派生类构造器的第一条语句,从而在派生类构造器调用的时候才调用"这个说法是错误的
    3)基类构造器调用就是派生类构造器的第一条语句,在派生类构造器调用的时候才调用。结果呢?再次证明这句话是错的。我明确对楼主说:你错了。
    这句话在不考虑“局部内部类访问final型局部变量时”这个条件下,是完全正确的
    所以说:只靠一些自己摸索例子,就归纳总结结论,而不去深究“编译程序内部到底是如何安排编译代码的”,才会导致楼主在云里雾里绕来绕去,变得很复杂。其实这个正说明:楼主自己似乎好象明白了,其实没有真正的明白。
      

  12.   

    创建一个类的对象的时候 首先是调用他父类的构造方法   没有父类 则调用OBJECT的  
      

  13.   


    静态初始化块
    static
    {
        //...
    }
    实例初始化块
    {
        //...
    }