本帖最后由 qiao_198911 于 2015-01-18 14:22:48 编辑

解决方案 »

  1.   

    涉及两点:
    一:Java初始化顺序
    1,在new 一个实例时首先要进行类的装载。(类只有在使用New调用创建的时候才会被java类装载器装入)
    2,在装载类时,完成静态动作(包括静态代码和变量,它们的级别是相同的,安装代码中出现的顺序初始化)
    类装载完成,开始进行实例化
    1,在实例化类时,先要成员实例化
    2,类的构造方法二:
    静态代码块只执行一次。
    构造代码块在每一次构造对象的开始执行,每构造一次都会执行一次。结合这两块知识应该就能分析出输出是如何产生的。
      

  2.   

    其实类的初始化过程和实例化过程的资料很多,最关键的一点是在静态变量的初始化中调用了类的构造器,这样,在实例化一个类之前要先初始化,而初始化是同步的,也就是说原来的new调用导致的类初始化尚未完成,所以对于类初始化的锁不会释放,再次new导致的类初始化会查看Class对象的标志,发现该类正在进行初始化,采取的行动是就像初始化已经完成一样,既然初始化已经完成,那么接着就是实例化了。实例化是先实例化成员变量和构造块,然后调用构造器。
    类的初始化顺序:
    如果得到初始化的机会,那么就会按照以下顺序执行(单指直接从Object继承的类,且无任何接口实现):
    1:初始化常量;
    2:初始化静态变量和静态块;
    以上都是按照文本顺序执行。
    另外,类的初始化并非只有调用new关键字一种。
      

  3.   

    楼主,我一句一句解释吧。
    因为除了Object,没有继承,Object加载先忽略了。
      
      public static int k=0;
      public static StaticTest s1=new StaticTest("s1");
      public static StaticTest s2=new StaticTest("s2");先加载静态成员变量,初始化为默认值,K为0,s1=null,s2=null,其他类似。
    然后准备给静态变量赋值,赋值到s1,这出现个对象,然后就转向来创建这个对象来。
    因为静态成员只初始化一次,所以静态成员不用初始化了。直接初始化实例变量和实例代码块。
    因而输出1:j i=0 n=0
    2:构造块 i=1 n=1 然后,最后构造函数初始化,所以3:s1 i=2 n=2
    输出6:之前类似。然后创建两个对象后又回来了,接着初始化赋值。
    赋值执行  public static int i=print("i");
     public static int n=99;
    此时已经初始化静态变量完毕。
    下面应该初始化静态块
     static
        {
            print("静态块");
        }
    然后所有静态都已经初始化完毕,然后初始化成员变量。最后调用构造方法。静态方法在第一次调用的时候就初始化。
    这个初始化是有嵌套的。
      

  4.   

    first, I am not a java developer but I think yiran_ming answered your question. I am not sure why you still feel confused there..."无法使用常理解释"According to jse specification, the class variable initializers and static initializers of the class, or the field initializers of the interface will be executed in textual order, as though they were a single block.so, field k will be assigned with 0 first. The next one is s1.with the type of "StaticTest". Java compiler now faces a dilemma here: it's supposed to initialize all static field first if the initializer is available, however, if it does so, then s1 has to be created first. That means an instance of class StaticTest would has to be created before initialzing the next static field (see above, executed in textual order). So, in order to avoid this "deadlock" or chicken egg problem, java compiler makes a compromise here: it will break the dilemma by initializing s1 and during the construction of s1, all "uninitialized" static fields will remain their default values, which are: 0 for all primitive type and null for reference type.does this code sample have any real practical value? I think it does. Considering the code below:
    import java.util.ArrayList;
    public class StaticTest {
        public static int k=0;
        public static StaticTest s1=new StaticTest("s1");
        public static StaticTest s2=new StaticTest("s2");
        public static ArrayList<String> al=new ArrayList<String>();
        public static int i=print("i");
        public static int n=99;
        public int j=print("j");
         
        {
            print("构造块");
        }
         
        static
        {
            print("静态块");
        }
         
        public static int print(String s)
        {
            System.out.println(++k+":"+s+"\ti="+i+"\tn="+n);
            ++n;
            return ++i;
        }
         
        public StaticTest(String s)
        {
            al.add("Oops!");
            System.out.println(++k+":"+s+"\ti="+i+"\tn="+n);
            ++i;
            ++n;
        }
     
        public static void main(String[] args) {
            new StaticTest("init");
        }
    }
    by the time s1 is being constructing, al is still null. So the attempt of adding an element to this list will cause a run-time exception! The compiler can't catch it at compilation period. C++ though, will throw a compile-time error:"undefined reference", but java and C# won't...comparing with C#, java made the thing even messy because it doesn't have a static constructor but has both initializer block and static initializer block, which in my opinion, bring more confusion than benefit in certain context... I don't want to be a dick here, but whoever invented this interview question is an asshole! But, since it's alibaba, they definitely have the right to pick smart asses who could solve this "simple" java puzzle...整体而言,有一个既定的步骤;但是代码运行无法使用常理解释,具体运行起来就有问题了......
      

  5.   

    具体的详细的过程整体而言,有一个既定的步骤;但是代码运行无法使用常理解释,具体运行起来就有问题了......不知道你说的常理是什么?这是很明确的,你去查看jls第十二章Excution吧。
      

  6.   

    http://docs.oracle.com/javase/specs/jls/se7/html/jls-12.htmldoes this chapter ever cover the situation we have in this code sample? I didn't find it. Let me know if you have the specific paragraph.
    ..
    整体而言,有一个既定的步骤;但是代码运行无法使用常理解释,具体运行起来就有问题了......不知道你说的常理是什么?这是很明确的,你去查看jls第十二章Excution吧。
      

  7.   

    整体而言,有一个既定的步骤;但是代码运行无法使用常理解释,具体运行起来就有问题了......不知道你说的常理是什么?这是很明确的,你去查看jls第十二章Excution吧。12.4.2. Detailed Initialization Procedure
    ........
    If the Class object for C indicates that initialization is in progress for C by the current thread, then this must be a recursive request for initialization. Release LC and complete normally.应该说的就是这事吧,俺英文不太好,还望大神多加指点,或者有理解不对的地方。
      

  8.   

    you are humble, "ooppookid" refers me as "没有实际项目经验". so, you are so kind!I am afraid what you posted here is not the one we are looking for:2. If the Class object for C indicates that initialization is in progress for C by some
    other thread, then release LC and block the current thread until informed that
    the in-progress initialization has completed, at which time repeat this step.
    3. If the Class object for C indicates that initialization is in progress for C by the
    current thread, then this must be a recursive request for initialization. Release
    LC and complete normally.3。如果代表类C的class对象实例正被当前调用线程初始化,说明当前的初始化请求(获取初始化锁LC)是一次递归调用,释放初始化锁并完成当前初始化工作。整体而言,有一个既定的步骤;但是代码运行无法使用常理解释,具体运行起来就有问题了......不知道你说的常理是什么?这是很明确的,你去查看jls第十二章Excution吧。12.4.2. Detailed Initialization Procedure
    ........
    If the Class object for C indicates that initialization is in progress for C by the current thread, then this must be a recursive request for initialization. Release LC and complete normally.应该说的就是这事吧,俺英文不太好,还望大神多加指点,或者有理解不对的地方。
      

  9.   

    The fact that initialization code is unrestricted allows examples to be constructed
    where the value of a class variable can be observed when it still has its initial default
    value, before its initializing expression is evaluated, but such examples are rare in
    practice. (Such examples can be also constructed for instance variable initialization
    (!ì12.).) =============
    这可能是你所指的吧。
      

  10.   

    你解答了我的一个疑问!
    开始我很不明白n值的变化是怎么发生的,现在我明白了
    静态的n值系统默认为0,执行public static StaticTest s1=new StaticTest("s1");
    s1是null系统要对该静态变量赋值,于是开始通过new 分配空间创建实例,此时是重复了开始的过程,此时静态代码都有了系统默认的初值,于是不再管静态变量,而直接到了第7行程序主动初始化非静态变量。这个子过程是程序整体过程中,主动给s1赋值的一部分;它的特殊之处在于,主动初始化静态字段的过程中,执行了非静态的语句和块;对s2的处理一样是一个子过程。
    现在看看n值的处理,当把s1,s2,i的值主动初始化完成以后,开始对n进行赋值,此时n的值是99了,然后是静态代码块!
    谢谢啊!基础不错!
      

  11.   

    I don't have the clue at all. I was not trying to challenge you! This paragraph might be the only place in this chapter that mentions the default value for class variable. I wish there could be a statement somewhere for resolving the conflict between the class initializer and instance initializer...
      

  12.   


    So, my reply on 16 楼 was not able to enlighten you? U owe me a thank u!Btw, I won't say something like “基础不错” as a compliment to someone who helped you! Show us an advanced topic that you might be good at but he isn't!你解答了我的一个疑问!
    开始我很不明白n值的变化是怎么发生的,现在我明白了
    静态的n值系统默认为0,执行public static StaticTest s1=new StaticTest("s1");
    s1是null系统要对该静态变量赋值,于是开始通过new 分配空间创建实例,此时是重复了开始的过程,此时静态代码都有了系统默认的初值,于是不再管静态变量,而直接到了第7行程序主动初始化非静态变量。这个子过程是程序整体过程中,主动给s1赋值的一部分;它的特殊之处在于,主动初始化静态字段的过程中,执行了非静态的语句和块;对s2的处理一样是一个子过程。
    现在看看n值的处理,当把s1,s2,i的值主动初始化完成以后,开始对n进行赋值,此时n的值是99了,然后是静态代码块!
    谢谢啊!基础不错!
      

  13.   

    你解答了我的一个疑问!
    开始我很不明白n值的变化是怎么发生的,现在我明白了
    静态的n值系统默认为0,执行public static StaticTest s1=new StaticTest("s1");
    s1是null系统要对该静态变量赋值,于是开始通过new 分配空间创建实例,此时是重复了开始的过程,此时静态代码都有了系统默认的初值,于是不再管静态变量,而直接到了第7行程序主动初始化非静态变量。这个子过程是程序整体过程中,主动给s1赋值的一部分;它的特殊之处在于,主动初始化静态字段的过程中,执行了非静态的语句和块;对s2的处理一样是一个子过程。
    现在看看n值的处理,当把s1,s2,i的值主动初始化完成以后,开始对n进行赋值,此时n的值是99了,然后是静态代码块!
    谢谢啊!基础不错!嵌套的new不再管静态变量随后的静态变量赋值,并不是因为它们都有了初值,这些初值也并不是在main中的new引发的,而是在初始化之前完成的。如果是因为静态变量都已经有了初值而不再理会初始化阶段,那main中的new应该也不会引起类的初始化了。关键的一点是,在未完成初始化的类上调用new,会采取什么行动?我觉得在new任何对象的时候,都会检查该类的初始化状态。如果初始化已经正常完成,那肯定是直接执行实例化阶段了。但是,初始化尚未完成,该怎么办?这是要有定义的,而不是简单的第一次调用new时初始化,如果第一次初始化标示类处于错误状态,那随后的new难道因为它是第二次new就不再查看类的状态,直接去实例化吗?对于其中的细节我没有研究,希望精通的大神讲解一下。
      

  14.   

    你先复制下来,在eclipse运行调试看看
      

  15.   

    I don't agree with you on this one. Class variable does have it "default value" regardless of the availability of static initializers! As I indicated previously, static variable with primitive type will be given a default value 0, reference type on the other hand,  will be "assigned" with null. a good analogy is the implementation of C, the uninitialized variable in .bss segment will be "initialized" by kernel to arithmetic 0 or null pointers.these static initializers are supposed to be invoked before any class instance is created. However, according to the textual order rule, if the instantiation of a class variable requires invoking instance initializers before the completion of static initializers, this particular instance will be in an "incomplete" status, which means the remaining static variables that is after this class variable will only have their "default value". Of course, this is my understanding but we failed to find this "rule" in official document to support ourselves.The "初值" you referred to is actually the "initialized variable". It's different from a variable with default value...my 2cents!
      

  16.   

    5.4.2 PreparationPreparation involves creating the static fields for the class or interface and initializing those fields to their standard default values (§2.5.1). Preparation should not be confused with the execution of static initializers (§2.11); unlike execution of static initializers, preparation does not require the execution of any Java virtual machine code.
      

  17.   

    the paragraph you referred to is probably out of date, this is the latest JLS:12.3.2 Preparation of a Class or Interface Type
    Preparation involves creating the static fields (class variables and constants) for
    a class or interface and initializing such fields to the default values (§4.12.5). This
    does not require the execution of any source code; explicit initializers for static
    fields are executed as part of initialization (§12.4), not preparation.See the part was removed: "preparation does not require the execution of any Java virtual machine code"This piece of verbiage proves that there are default values for static fields in the class during the preparation stage(§4.12.5). However, this particular behavior on this code sample, we only have observation...
    5.4.2 PreparationPreparation involves creating the static fields for the class or interface and initializing those fields to their standard default values (§2.5.1). Preparation should not be confused with the execution of static initializers (§2.11); unlike execution of static initializers, preparation does not require the execution of any Java virtual machine code.
      

  18.   

    你解答了我的一个疑问!
    开始我很不明白n值的变化是怎么发生的,现在我明白了
    静态的n值系统默认为0,执行public static StaticTest s1=new StaticTest("s1");
    s1是null系统要对该静态变量赋值,于是开始通过new 分配空间创建实例,此时是重复了开始的过程,此时静态代码都有了系统默认的初值,于是不再管静态变量,而直接到了第7行程序主动初始化非静态变量。这个子过程是程序整体过程中,主动给s1赋值的一部分;它的特殊之处在于,主动初始化静态字段的过程中,执行了非静态的语句和块;对s2的处理一样是一个子过程。
    现在看看n值的处理,当把s1,s2,i的值主动初始化完成以后,开始对n进行赋值,此时n的值是99了,然后是静态代码块!
    谢谢啊!基础不错!嵌套的new不再管静态变量随后的静态变量赋值,并不是因为它们都有了初值,这些初值也并不是在main中的new引发的,而是在初始化之前完成的。如果是因为静态变量都已经有了初值而不再理会初始化阶段,那main中的new应该也不会引起类的初始化了。关键的一点是,在未完成初始化的类上调用new,会采取什么行动?我觉得在new任何对象的时候,都会检查该类的初始化状态。如果初始化已经正常完成,那肯定是直接执行实例化阶段了。但是,初始化尚未完成,该怎么办?这是要有定义的,而不是简单的第一次调用new时初始化,如果第一次初始化标示类处于错误状态,那随后的new难道因为它是第二次new就不再查看类的状态,直接去实例化吗?对于其中的细节我没有研究,希望精通的大神讲解一下。
    你提出了两个问题:
    1、“在未完成初始化的类上调用new”
    这个问题就是一个疑点;如果本例中的s1,s2对象不是静态的,那么因为递归调用缘故,会导致栈溢出。
    2、系统默认值在什么时候赋予的?你提出不是调用new,在本例中,系统对类的静态变量的初始化,的确不是new,而是main方法引发的。如果在一个类里面执行main方法,那么会引起类的加载和系统默认初始化行为。
    我们再次梳理一下
    在main方法中语句:(1)new StaticTest("init");和类的字段赋值语句(2)new StaticTest("s1");二者都是要创建类的对象。
    main方法中的语句(1)执行的很晚很晚,因为main方法想要运行,系统发现类竟然还没载入,于是载入类并进行系统默认的初始化行为;但是这个系统初始化行为中间发生了递归初始化。
    系统载入类,接着系统自动完成类的默认初始化,此时的值就是0值和null值两类;
    既然是初始化,我们要注意两大类:静态的和非静态的;在载入、初始化的过程中,如果没有使用new 的方式创建对象,那么系统是不会对类的非静态变量进行默认初始化的,即使是0值和null值,也没有,JVM根本就不会理会非静态变量。现在使用了new,那么初始化就会涉及到对非静态变量的初始化。
    运行开始
    main方法激发类的加载,紧接着系统对静态变量完成了默认初始化,静态对象类型是null值,普通类型是0值;非静态变量,还没有进行任何处理,连默认值也没有;此时JVM还没对非静态变量做任何处理;
    首先对k进行初始化,k的系统默认初始化值是0,此时进行赋值,再次被赋值为0,
    到了(2)系统此时发现s1是一个对象,值为null(这个null是系统默认初始化得到的),要对s1进行赋值;这个地方就发生了你提出的问题1,即“在未完成初始化的类上调用new” 系统如何处理的呢?我的看法是:系统按照正常流程走,跟在其他地方使用new创建对象完全一样。
    这里就是嵌套初始化或者叫做递归初始化:在初始化自己的过程中,初始化自己。
    系统会进行类的加载,并进行静态变量的初始化,但是系统发现类的静态变量已经完成了初始化,此时是使用了new 创建对象会涉及到非静态变量的初始化,那么直接到第七行,执行非静态变量的初始化。
    有问题,继续交流!
      

  19.   


    5.4.2 PreparationPreparation involves creating the static fields for the class or interface and initializing those fields to their standard default values (§2.5.1). Preparation should not be confused with the execution of static initializers (§2.11); unlike execution of static initializers, preparation does not require the execution of any Java virtual machine code.这不是说明了为什么n是0,而不是99吗?如果初始化完成,它就是99。0这个值当然不是在初始化阶段赋予的。
      

  20.   


    我砸场子贴在此:http://bbs.csdn.net/topics/390971281
    http://bbs.csdn.net/topics/390963458
      

  21.   

    are you talking to me or qiao_198911?5.4.2 PreparationPreparation involves creating the static fields for the class or interface and initializing those fields to their standard default values (§2.5.1). Preparation should not be confused with the execution of static initializers (§2.11); unlike execution of static initializers, preparation does not require the execution of any Java virtual machine code.这不是说明了为什么n是0,而不是99吗?如果初始化完成,它就是99。0这个值当然不是在初始化阶段赋予的。
      

  22.   


    5.4.2 PreparationPreparation involves creating the static fields for the class or interface and initializing those fields to their standard default values (§2.5.1). Preparation should not be confused with the execution of static initializers (§2.11); unlike execution of static initializers, preparation does not require the execution of any Java virtual machine code.这不是说明了为什么n是0,而不是99吗?如果初始化完成,它就是99。0这个值当然不是在初始化阶段赋予的。我当然是和你说,我并没否认你引用的内容,我引用的那段来自jvm规范,它们之间有矛盾吗?
      

  23.   


    我砸场子贴在此:http://bbs.csdn.net/topics/390971281
    http://bbs.csdn.net/topics/390963458看来我得闪哪
      

  24.   

    sorry, I am not sure why you raise the argument on the value on variable n.I thought we both agree that default value exists for static variables. I might misunderstood you when you said:"嵌套的new不再管静态变量随后的静态变量赋值,并不是因为它们都有了初值,这些初值也并不是在main中的new引发的,而是在初始化之前完成的。".After the second read, I got what you meant. But I would suggest you replacing "初值" with "缺省值",which could completely rule out any misunderstanding...are we on the same page now?5.4.2 PreparationPreparation involves creating the static fields for the class or interface and initializing those fields to their standard default values (§2.5.1). Preparation should not be confused with the execution of static initializers (§2.11); unlike execution of static initializers, preparation does not require the execution of any Java virtual machine code.这不是说明了为什么n是0,而不是99吗?如果初始化完成,它就是99。0这个值当然不是在初始化阶段赋予的。我当然是和你说,我并没否认你引用的内容,我引用的那段来自jvm规范,它们之间有矛盾吗?
      

  25.   

    I can ensure you my target is not you!BTW, thank you for showing me the paragraph you found in jvm spec, it's quite helpful!我砸场子贴在此:http://bbs.csdn.net/topics/390971281
    http://bbs.csdn.net/topics/390963458看来我得闪哪
      

  26.   


    5.4.2 PreparationPreparation involves creating the static fields for the class or interface and initializing those fields to their standard default values (§2.5.1). Preparation should not be confused with the execution of static initializers (§2.11); unlike execution of static initializers, preparation does not require the execution of any Java virtual machine code.这不是说明了为什么n是0,而不是99吗?如果初始化完成,它就是99。0这个值当然不是在初始化阶段赋予的。我当然是和你说,我并没否认你引用的内容,我引用的那段来自jvm规范,它们之间有矛盾吗?抱歉,我用词却有不妥,意思你已明白,共识算达成了吧。
      

  27.   

    Do not go ah. . Let me a while to learn English.
    我擦,真的有6个套套哦!尼玛,你来答一下好了!
      

  28.   

    简单点解释就是:静态变量首先加载,结果在初始化静态变量s1时,调用实例化对象方法。导致s1之后的静态变量初始化暂停,转而初始化实例变量(注:此时静态变量的初始化就暂停了 直接跳到public int j=print("j");)。
    所以1-3行输出结果是public static StaticTest s1=new StaticTest("s1");导致。
    所以4-6行输出结果是继续初始化静态变量public static StaticTest s2=new StaticTest("s2");导致。
    7-8行输出结果为继续初始化静态变量public static int i=print("i");
        public static int n=99;
        public int j=print("j");导致
    9-10行输出结果为main方法内导致!