小弟有一个java中初始化成员的问题Thinking in Java 这本书中说过,
所有的成员变量都是在调用构造函数之前进行初始化的
但是在继承中为什么就不是这种情况了呢?代码如下:class Test{
Test(){
System.out.println("The TestClass Print!");
}
}class FatcherClass{
public long test;

FatcherClass(){
System.out.println("The FatcherClass Print! The Value = " + test);
}
}
class SunClass extends FatcherClass{

SunClass(){
System.out.println("Test SunClass Print!");
}
}public class ExtendsClassTest extends SunClass { private Test t =  new Test();    
    
    ExtendsClassTest(){
     System.out.println("The ExtendsClassTest Print!");
    }
    
    public static void main(String[] ages){
     ExtendsClassTest sClass = new ExtendsClassTest();
    }
    
}上述的执行结果为什么是
The FatcherClass Print! The Value = 0
Test SunClass Print!
The TestClass Print!
The ExtendsClassTest Print!而不是
The TestClass Print!
The FatcherClass Print! The Value = 0
Test SunClass Print!
The ExtendsClassTest Print!
这个结果呢?????
希望各位大大们帮着解释下

解决方案 »

  1.   

    在调用自己的构造方法之前都先调用直接父类的构造方法。
    在调用构造方法之前保证所有的initialization都已经做完。
      

  2.   


    class Test {
    Test() {
    System.out.println("The TestClass Print!");
    }
    }class FatcherClass {
    public long test; FatcherClass() {
    System.out.println("The FatcherClass Print! The Value = " + test);
    }
    }class SunClass extends FatcherClass { SunClass() {
    System.out.println("Test SunClass Print!");
    }
    }public class ExtendsClassTest extends SunClass { private Test t = new Test(); ExtendsClassTest() {
    System.out.println("The ExtendsClassTest Print!");
    } public static void main(String[] ages) {
    ExtendsClassTest sClass = new ExtendsClassTest();
    }}在继承中,首先得执行这个子类的父类的构造函数,如果子类中有成员变量,那么将在子类的构造函数的前面进行初始化.
      

  3.   

    以前回答过类似的问题,直接复制给你...楼主的代码是TIJ里的吧,以前看的,忘了。涉及初始化问题,全世界就TIJ这本书讲的最牛。 java编译器确保域(成员变量或块)在被使用之前必需初始化,即使你没直接初始化,它也会默认给你一个值。 1. 
      如果不涉及继承跟static,用new创建对象时,会调用那个类的构造函数,而在调用构造函数之前必需先初始化域(因为在构造函数里,可能会用到这些成员变量) 
    等域初始化完后再调用构造函数。强调一点:只要是成员变量,那么不管它放在类的哪个部位(但在方法或块内部不算,因为那算是局部变量),它都在构造函数调用之前调用,这是编译器确保的。 2. 
    如果涉及继承,当用new创建子类对象时,调用顺序是这样的: 
      1.先初始化父类的域(成员变量或块) 
      2.调用父类的构造函数(没有明确定义的话,调用默认那个,即编译器为你创建的) 
      3.再到子类,初始化子类的域 
      4.这时才轮到调用子类的构造函数 
    原则是:要确保域在被调用之前要被初始化. 
    上面是涉及两层,如果是涉及多层继承的,那么一致递推上去,即先初始化父类的域,然后调用父类构造函数,再初始化子类的域然后再调用子类的构造函数,再初始化子子类的域(用这个名字好像有点怪,哈哈,就是孙子类的意思)然后再调用子子类的构造函数,一致类推下去 
    3.涉及static的话,static域是在编译的时候加载的,原则是: 
      1.static域是在非static(上面说的都是非static)之前调用的 
      2.static域只初始化一次(即只调用一次),打个比方A a = new A(); A 里有static域,只有当你第一次使用new创建对象的时候它会在非static之前调用,而如果你还想再用new创建对象时,static域这段代码是不会被调用的(因为static的东西是属于类,所以对象共享的,一次就够了) 4.如果涉及继承跟static结合的话(而这个是初始化里最难的,很多初学者会卡在这里),只要按照3.2结合就行了。 
      

  4.   

    楼主,你可以这么理解:继承就是在父类的基础上加工出子类。所以,如果有继承关系,父类的一切初始化动作都必须完成,才能进行子类的初始化,
    所以,成员变量和构造函数是有先后,但父子关系的先后是大前提。这也是作为一个class的历史局限性吧,呵呵。
      

  5.   

    解释是这样的:
    在java中有四块区域1:static data segment(存放static数据和static方法...);
    2:stack segment (速度比较快,存放临时的数据);
    3:code segment(存放Class文件)
    4:heap(存放new出来的类 垃圾产生和回收的地方)在程序运行以前 先加载 static 的域  在static data segment里面开辟内存
    最典型的就是public  static void main(String []arg){}
    所以一般认为是从面开始运行的,但是并不完全是这样 5楼说的比较详细,也比较全面你就这样理解吧: 先实现父类的静态域,静态方法(必须调用才能动态执行)---然后再就是父类的一般域   和方法(方法必须调用才能动态执行)
     接着就是子类的静态域,静态方法(必须调用才能动态执行)---然后再就是子类的一般域   和方法(方法必须调用才能动态执行)至于前面4个块 的话 你可以在维基百科里面查我就点到为止
    养成自己动手解决问题的好习惯!这个很重要!希望我的解释对你有用!
      

  6.   

    写代码测试,是这个结果,一看便知public class Test2 extends Father{

    static{
    System.out.println("Test2.静态块");
    }

    {
    System.out.println("Test2.程序块");
    }

    public Test2(){
    System.out.println("Test2.构造方法");
    }
    Other o=new Other("Test2");
    public static void main(String [] args){
    Test2 test=new Test2();
    }}class Father extends Ffather{
    static{
    System.out.println("Father.静态块");
    }
    Other o=new Other("Father");

    {
    System.out.println("Father.程序块");
    }

    Father(){
    System.out.println("Father.构造方法");
    }
    }class Ffather{
    static{
    System.out.println("Ffather.静态块");
    }
    Other o=new Other("Ffather");

    {
    System.out.println("Ffather.程序块");
    }

    Ffather(){
    System.out.println("Ffather.构造方法");
    }
    }class Other{
    static{
    System.out.println("Other.静态块");
    }

    {
    System.out.println("Other.程序块");
    }

    Other(String str){
    System.out.println("Other.构造方法  "+str+"调用");
    }
    }//执行结果
    Ffather.静态块
    Father.静态块
    Test2.静态块
    Other.静态块
    Other.程序块
    Other.构造方法  Ffather调用
    Ffather.程序块
    Ffather.构造方法
    Other.程序块
    Other.构造方法  Father调用
    Father.程序块
    Father.构造方法
    Test2.程序块
    Other.程序块
    Other.构造方法  Test2调用
    Test2.构造方法
      

  7.   

    楼主可以按照之前5楼的方式来理解,他讲的很详细了,我在这里只说说更底层一点的东西,也就是调用构造函数的真实实现方式。
    首选在用new关键字后跟构造函数的语句生成实例的时候会经过以下过程。
    首先虚拟机会检查生成实例的类有没有被初始化(前提是这个类已经被虚拟机装载,如果装载失败此时应该会抛出classnotfoundexception异常),如果没有被初始化,虚拟机会检查类有没有初始化方法(类初始化方法,即"<clinit>"方法,这是一个由编译器产生,且只能由虚拟机内部调用的静态方法,这个方法并不是所有类都有,是只有初始化语句的类才有这个方法的,初始化语句就是静态字段=后面的表达式语句以及static语句块中的语句,编译器会将这些语句添加到"clinit"方法中),如果有初始化方法就调用初始化方法,如果没有则跳过这个过程。在此之前,虚拟机会检查这个类的父类有没有被初始化,如果父类没有被初始化,先初始化父类。
    类初始化以后,接着就是给生成的对象分配内存空间,分配完成以后执行对象的构造函数,很多人会疑惑,这个地方为什么没有说先去执行父类构造函数,或者说先初始化对象的实例字段,因为很简单,在进入当前类构造函数的时候的第一个语句就是调用父类构造函数,除了java.lang.Object类的构造函数以外,所有构造函数都是如此。然后执行构造函数内的初始化语句。这里又有人疑问,那类的字段初始化与初始化块在哪里,其实这些语句就在构造函数内。编译器在编译java文件生成class的时候,会将字段初始化语句,初始化块以及构造函数内的语句复制到构造函数内,而且是有一定顺序的,首先将把初始化字段语句与初始化块复制到调用父类构造函数之下,紧接着在吧当前构造函数语句(.java文件中的构造函数居于)复制到构造函数内(.class文件的构造函数),而字段与语句块的执行顺序则是.java文件中他们出现的顺序(这里你不妨实验一下,看看在某个字段的=操作符后面能不能使用在其后面声明的字段,很明显就是不能的,编译无法通过)。
    经过以上过程,就生成一个实例了。
      

  8.   

    JAVA执行顺序:
    父类静态属性(方法),子类静态属性(方法)
    父类成员属性(方法),子类属性(方法)
    父类构造属性(方法),子类属性(方法)
      

  9.   

    step1:javac ExtendsClassTest.java时,会产生4个class文件。
    分别是Test.class  FatcherClass.class SunClass.class ExtendsClassTest.class
    step2:java ExtendsClassTest
    试图加载ExtendsClassTest.class,然而由于有父类SunClass(通过extends关键字得知),此时,
    “中断加载”ExtendsClassTest.class,立马加载SunClass.class,然而也由于它也有父类FatcherClass,此时,“中断加载”SunClass.class,立马加载FatcherClass。
    实际上,加载类文件顺序为:FatcherClass.class,SunClass.class,ExtendsClassTest.class.