问题1:类的初始化顺序public class Test1 {
public static int k=0;
public static Test1 t1=new Test1("t1");
public static Test1 t2=new Test1("t2");
public static int i=print("i");
public static int n=99;

public int j=print("j");

{
print("构造块");
}

static{
print("静态块");
}

public Test1(String str){
System.out.println((++k)+":"+str+"    i="+i+"    n="+n);
++i;++n;
}

public static int print(String str){
System.out.println((++k)+":"+str+"    i="+i+"    n="+n);
++n;
return ++i;
}

public static void main(String...strings ){

}
}输出结果:
1:j    i=0    n=0
2:构造块    i=1    n=1
3:t1    i=2    n=2
4:j    i=3    n=3
5:构造块    i=4    n=4
6:t2    i=5    n=5
7:i    i=6    n=6
8:静态块    i=7    n=99问题:i和n到底是什么时候声明的,public static Test1 t1=new Test1("t1");这句会去执行构造块和构造方法,但是这时候public static int i=print("i");并没执行,i是哪里出来的?它是按顺序先声明所有变量然后才从头再挨个赋值吗?问题2:enum的构造方法里为什么不能引用静态变量public enum Test2 {
A,B,C,D,E,F,G;
public static int i=11;
private Test2(){
i=22;
}
}我从the java programming language里找了一段解释,但是看的不太明白,而且感觉解释的很浅,有点转不过来弯,麻烦高人来讲解下原因,最好能举个例子,像我上面的那段代码,我感觉没什么不可以阿
There are three restrictions on the definition of an enum constructor:All enum constructors are private. While you can use the private access modifier in the constructor declaration, by convention it is omitted. Private constructors ensure that an enum type cannot be instantiated directly.The constructor cannot explicitly invoke a superclass constructor. The chaining to the super constructor is handled automatically by the compiler.An enum constructor cannot use a non-constant static field of the enum.This last restriction requires a little explanation. Because each enum constant is a static field of the enum type, the constructors are executed during static initialization of the enum class. The enum constant declarations must be the first declarations in the type, so the constructors for these values will always be the first code executed during static initialization. Any other static fields will be initialized afterward. So if a constructor were to refer to a static (non-constant) field of the enum, it would see the default uninitialized value. This would nearly always be an error, and so it is simply disallowed.

解决方案 »

  1.   

    问题1很简单,自己单步看一下即可.
    问题2看一下enum的具体实现即可,我的分析:http://blog.csdn.net/ZangXT/archive/2008/10/29/3174741.aspx
      

  2.   

    理解:static的常量是按照在类定义中出现的顺序来实现的,执行第一个的时候需要执行构造方法,而执行构造方法的时候看到的在这个static定义后面的静态变量是没有初始化的.
    直接看我的blog中的代码理解即可.
      

  3.   

    问题1:由于你声明的是static类型的变量,所以这些变量在你new一个对象的时候就会自动初始化,如果你在类中没有在声明变量的同时给变量赋值,那么它就会给该变量自动赋值一个值,int型的默认赋值是0,String的默认是null等,应该是在声明的时候如果没有赋值就自己赋值了,并不是像你说的那样先全声明再来一一赋值问题2:不太懂,几乎没有像你这样声明过enum
      

  4.   

    我每步都print了 不需要单步 
    什么时候执行的public static int i=print("i");已经打印的很清楚了 
      

  5.   

    1楼的那个enum过程我知道 不过我还是不清楚构造方法里为什么不能引用静态变量 给出足够的理由先 谢谢
      

  6.   

    不懂enum
    第一个单步or输出lz应该知道顺序吧
      

  7.   

    早就仔细看过了,没发现难点在什么地方.
    看到你的print的了 ,我的意思是说你单步执行就可以发现执行原理了.每次进入的是哪一句话就很清楚了.
      

  8.   

    先把非static初始化清理一下,变成这个样子再分析public class Test1 {
        public static int k=0;
        public static Test1 t1=new Test1("t1");
        public static Test1 t2=new Test1("t2");
        public static int i=print("i");
        public static int n=99;    public int j;    static{
            print("静态块");
        }    public Test1(String str){
            j=print("j");
            print("构造块");
            System.out.println((++k)+":"+str+"    i="+i+"    n="+n);
            ++i;++n;
        }    public static int print(String str){
            System.out.println((++k)+":"+str+"    i="+i+"    n="+n);
            ++n;
            return ++i;
        }    public static void Test1(String...strings ){    }
    }
      

  9.   

    因为j没static 执行构造方法前会先执行那些语句 这个我清楚 但是static的不一样啊 如果static的也是这样 那就是我开始说的 先把所有变量声明了 然后再从头执行赋值语句??
      

  10.   

    问题:i和n到底是什么时候声明的,public static Test1 t1=new Test1("t1");这句会去执行构造块和构造方法,但是这时候public static int i=print("i");并没执行,i是哪里出来的?它是按顺序先声明所有变量然后才从头再挨个赋值吗?
    关于这个问题:类的生命周期分为如下几个阶段,Loading,Linking,Initialization.
    Loading之后会对class文件进行Verification,也就是验证.然后是Preparation阶段,注意在这个阶段所有的类变量,也就是class变量,会被默认初始化,也就是int,long,short,浮点等基本类型初始化为0,引用初始化为null,boolean被初始化为false.也就是到这个阶段各个static变量已经有默认的值了,后面会进入Resolution阶段,然后才是初始化,即Initialization,这个阶段会执行static初始化,把变量初始化为你指定的值.也就是在这个阶段k=0;Test1 t1=new Test1("t1");才开始执行的,这时候i=print("i");还没有执行到,i仍然取在 Preparation阶段得到的默认值0.
    这样应该清楚了吧.
      

  11.   

    你就直接跟我说具体是怎么回事就可以了 别绕那么多弯啊 thinking in java 上面关于类的初始化顺序我已经掌握了 但是那上面没解释我的问题
      

  12.   

    这个可以参考一下java虚拟机规范,或者深入java虚拟机
    可以看看这个: http://www.artima.com/insidejvm/ed2/lifetype.html
      

  13.   

    第二个问题看我的blog,上面提供链接了,知道enum到底怎么实现的即可. 
      

  14.   

    其实你的第一个例子很好,enum也是类似,就是在静态初始化的时候调用了构造方法,如果你在这个方法里使用了非final的静态变量,这时候的值还不是你初始化后的值,java为了避免这种不确定性,干脆禁止你这么干了.
      

  15.   

    3Q  第一个问题理解了 it would see the default uninitialized value这句话理解起来就容易多了 谢谢
      

  16.   

    不错,ZangXT 和果子一样有研究精神。 各位网友真是好福气啊!
      

  17.   

    昨天不是有个分析JAVA的静态变量的帖子吗?LZ可以学一下
      

  18.   

    不懂enum 
    第一个单步or输出lz应该知道顺序吧
      

  19.   

    public static void main(String...strings ){
            
        }
    我还重来没见过 这样写的代码??
      

  20.   

    本人认为初始化顺序是(如不对请指教):1.当你载入一个类时如果不进行任何设置先进行父:static--》子static的初始化。。
    2.当出现第一个new 时进行父:属性--》子属性的初始化(非static,第一步初始已经完了)。。其中注意属性中的new。。若存在则先调用他对应的的构造函数。
    3. 2后 父:构造器--》子构造器的初始化
      

  21.   

    请问 public static void main(String...strings )中(String...strings )是什么意思?
      

  22.   

    public class Test1 {
        public static int k=0;
        public static Test1 t1=new Test1("t1");
        public static Test1 t2=new Test1("t2");
        public static int i=print("i");
        public static int n=99;
        
      //  public int j=print("j");
        
        {
            print("构造块");
        }
        
        static{
            print("静态块");
        }
        
        public Test1(String str){
            System.out.println((++k)+":"+str+"    i="+i+"    n="+n);
            ++i;++n;
        }
        
        public static int print(String str){
            System.out.println((++k)+":"+str+"    i="+i+"    n="+n);
            ++n;
            return ++i;
        }
        
        public static void main(String...strings ){
            
        }
    }
    结果是:
    1:构造块    i=0    n=0
    2:t1    i=1    n=1
    3:构造块    i=2    n=2
    4:t2    i=3    n=3
    5:i    i=4    n=4
    6:静态块    i=5    n=99
      

  23.   

    基本上相当于public static void main(String[] strings )
    但是有点一好处,如下:
    void test(String... strings){
    // 这里面取值和数组一样
    System.out.print(strings.length);
    for(String str : strings) {
    System.out.print(str+",");
    }
    }调用的时候下面这些写法都可以。
    test("a");
    test("a", "b");
    test("a", "b", "c");
    test("a", "b", "c", "d");
    ...
      

  24.   

    实例化一个类的过程
       第一步:JVM会装载需要用到的类,类在被装载的时候,会先运行静态代码块(只要被static修饰的方法,变量。都会在被实例化之前装入);JVM装载类的顺序是从基类(Object)开始的; 
       第二步:实例化类。在实例化类的时候会先执行实例代码块之后再调用构造函数,通常是使用new关键字,调用类的构造函数;除Object类外,其他类都需要调用父类的构造方法super()(隐式的);如果该类的父类还有父类,则需要调用父类的super()构造函数,直到根类Object为止。从方法栈可以看出,实例化是从基类开始的。 public class TestCh3_6 extends Nic{
    public static void main(String[] args) {
    System.out.println("pre ");
    new TestCh3_6();
    System.out.println("end ");
    }
    }
    class Bird{
    static{System.out.println("classLoder this Bird");}
    {System.out.println("b1 ");}
    Bird(){
    System.out.println("b2 ");
    }
    }
    class Nic extends Bird{
    static {System.out.println("r1 ");} 
    public Nic(){System.out.println("r2 ");}
    {System.out.println("r3 ");}
    static {System.out.println("r4 ");}
    }
    分析:TestCh3_6 extends Nic;Nic extends Bird;Bird extend Object;
    程序在进入mian()之前要装载4个类,TestCh3_6,Nic,Bird,Object
    装载Object,之后装载Bird,运行静态代码块,print:classLoder this Bird
    装载Nic,运行静态代码块,print:r1 r4 (按照代码块在上下文的顺序运行)
    装载TestCh3_6。装载完毕;
    运行mian(),print: pre;
    实例化TestCh3_6,隐式递归调用super();直到Object();
    实例化完Object之后,实例化Bird,先运行实例代码块 print:b1
    之后运行构造函数 Bird();print:b2 实例化结束;
    实例化Nic,先运行实例代码块print:r3
    之后运行构造函数Nic();print:r2;
    最后运行testch3_6的构造函数;实例化完成
    运行下一条语句,print: end
    退出main()
      

  25.   

    void test(String... strings){
    // 这里面取值和数组一样
    System.out.print(strings.length);
    for(String str : strings) {
    System.out.print(str+",");
    }
    }调用的时候下面这些写法都可以。
    test("a");
    test("a", "b");
    test("a", "b", "c");
    test("a", "b", "c", "d");
    ...
      

  26.   

    There are three restrictions on the definition of an enum constructor: 有三个限制枚举的构造函数的定义:All enum constructors are private. While you can use the private access modifier in the constructor declaration, by convention it is omitted. Private constructors ensure that an enum type cannot be instantiated directly. 所有枚举的构造函数是私人的。当你可以使用私人访问改性剂在构造函数声明,按照惯例是省略了。私人承建商确保一个枚举类型不能直接被实例化。The constructor cannot explicitly invoke a superclass constructor. The chaining to the super constructor is handled automatically by the compiler. 构造函数调用父类的构造函数不能明确。超级链接的构造器是由编译器自动处理。An enum constructor cannot use a non-constant static field of the enum. 一个枚举的构造函数不能用一个静态域变的枚举。This last restriction requires a little explanation. Because each enum constant is a static field of the enum type, the constructors are executed during static initialization of the enum class. The enum constant declarations must be the first declarations in the type, so the constructors for these values will always be the first code executed during static initialization. Any other static fields will be initialized afterward. So if a constructor were to refer to a static (non-constant) field of the enum, it would see the default uninitialized value. This would nearly always be an error, and so it is simply disallowed. 最后简单说明限制要求。因为每个枚举常数是一个静态的领域的枚举类型,在静态构造函数执行枚举类初始化。枚举常数的声明要先声明中所涉及到的这种类型,所以车队为这些价值观总会第一个在静态初始化代码执行。任何其它的静态初始化之后领域。所以如果一个构造函数是指一个静态的(非常)领域的过程,它会看到默认未初始化的价值。这将几乎永远是一个错误,所以它只是被判无效。
      

  27.   


    我还是不太明白输出结果为什么先输出:1:j    i=0    n=0,静态变量不是在非静态变量之前初始化吗?
      

  28.   

    我不是很懂jvm原理与编程思想,只是根据结果来推断:
       1.类被调用:(针对这个例子就是main方法启动)
         A:加载类变量,默认初始化(从上至下)
         B:给类变量赋值,以及加载static代码块(从上至下)
       仅被加载一次   2.创建对象(针对new运算符)
         A:加载成员变量,默认初始化
          B:给成员变量初始化,以及加载代码块
          C:调用构造器就这个例子而言:先是加载int k , Test1 t1 , Test1 t2 , int i , int n 并给默认值
                  再是k=0 , t1=new Test1("t1") 
       当new Test1("t1") 时 先是加载int j 并给默认值
                               再是j=print('j') 调用print()方法 打印出 1:j i=0 n=0
                            再调用代码块 打印出       2:构造块  i=1  n=1
                            再调用构造器 打印出        3:t1    i=2    n=2
                  t2=new Test1("t2")
       当new Test1("t2") 时 先是加载int j 并给默认值
                               再是j=print('j') 调用print()方法 打印出 4:j i=3 n=3
                            再调用代码块 打印出            5:构造块    i=4    n=4
                            再调用构造器 打印出            6:t2    i=5    n=5
                   i=print("i")
                           调用print()方法 打印出          7 :i    i=6    n=6 
                   此时的i=7了  n=99
                   再加载静态代码块 打印出:   8:静态块    i=7    n=99