静态变量初始化的顺序,Java

16
当我运行这段代码时,答案是1,我以为会是2。 初始化的顺序和每个步骤中k的值是多少? Possible Duplicate: Java静态类初始化 静态块和静态变量在类中的执行顺序是什么?
public class Test {

    static {k = 2;}
    static int k = 1;

    public static void main(String[] args) {
        System.out.println(k);
    }
}

编辑1:作为“k设置为默认值”的后续操作,为什么下面的代码不能编译?出现错误“在定义之前不能引用字段”。
public class Test {

    static {System.out.println(k);}
    static int k=1;

    public static void main(String[] args) {
        System.out.println(k);
    }
}

编辑2:由于某些我不知道的原因,当使用“ k”时,它无法正常工作,但当使用“ Test.k”时可以正常工作。
感谢所有的回答,这已经足够了:D

1
首先,k将被分配默认值,然后从顶部开始运行静态代码,2将被分配,然后1将被分配。然后调用主函数并打印出k。static int k = 1实际上是static int k;声明,然后是static { k = 1; } - nhahtdh
@KennyTM 是的,如果我按照你贴出来的那篇帖子的方式运行它,我就会知道发生了什么。 - Andrzej Rehmann
@nhahtdh k会被分配默认值吗?那为什么这段代码不能编译:public class Test { static {System.out.println(k);k=2;} static int k ; public static void main(String[] args) { System.out.println(k); } } - Andrzej Rehmann
1
@Hoto:(使用Test.k打印它)。我没有足够的知识来解释整个情况。从这个链接:1)final字段将根据初始化程序进行初始化2)静态初始化程序和静态块将按照文本顺序运行(在块运行之前,静态字段将具有默认值) - nhahtdh
一个演示我的观点的测试程序:http://ideone.com/GiArqV,以及关于[初始值](http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.12.5)的说明。 - nhahtdh
@nhahtdh 谢谢。现在我得找出为什么它只能与“Test.k”一起使用;) 无论如何,您应该以“答案”而不是“评论”开始回答。您的答案是最好和最快的。 - Andrzej Rehmann
2个回答

11

它们按照你编写的顺序执行。如果代码是:

public class Test {

    static int k = 1;
    static {k = 2;}

    public static void main(String[] args) {
        System.out.println(k);
    }

}
然后输出为2。
初始化顺序为:类变量的初始化器和静态初始化器,按照文本顺序,就像它们是一个单一的块。 对于你的代码,变量的值为:k = 0(默认值),然后被设置为2,然后被设置回1。
您可以通过运行以下代码来检查它是否实际设置为2:
private static class Test {

    static {
        System.out.println(Test.k);
        k = 2;
        System.out.println(Test.k);
        }
    static int k = 1;

    public static void main(String[] args) {
        System.out.println(k);
    }
}

这不是我在问题中想要表达的意思。我想要每一步初始化和k值的顺序。 - Andrzej Rehmann
好的,我添加了更详细的解释。 - tibtof
是的...但现在你的回答有点混乱 xD - Andrzej Rehmann
k 是否被设置为默认值?这段代码无法编译:public class Test {static {System.out.println(k);k=2;} static int k ; public static void main(String[] args) { System.out.println(k); }} - Andrzej Rehmann
1
这段代码的作用是:public class Test { static {System.out.println(Test.k);k=2;} static int k ; public static void main(String[] args) { System.out.println(k); } } - tibtof
不错的,我的朋友 :D 所以它确实有一个初始值为0。 - Andrzej Rehmann

5

简短回答

当类初始化开始时,k 的初始值为 0。

静态块(因为它在声明中的赋值之前)随后被执行,k 将被赋值为 2。

然后执行声明中的初始化器,k 将被赋值为 1。

详细解释

让我们使用 this example 来说明,因为您的示例有点简单:

class TestInitOrder {
  static {
    System.out.println(TestInitOrder.stat1);
    System.out.println(TestInitOrder.stat2);
    System.out.println(TestInitOrder.str);
    System.out.println(TestInitOrder.str2);

    str = "something";

    System.out.println(TestInitOrder.str);
    System.out.println(TestInitOrder.str2);
    System.out.println(TestInitOrder.lazy);
    System.out.println(TestInitOrder.second);
  }

  private static final int stat1 = 10;
  static final String str2 = "sdfff";
  static String str = "crap";
  private static int stat2 = 19;
  static final Second second = new Second();
  static final int lazy;

  static {
    lazy = 20;
  }

  static {
    System.out.println(TestInitOrder.str2);
    System.out.println(TestInitOrder.stat2);
    System.out.println(TestInitOrder.str);
    System.out.println(TestInitOrder.lazy);
    System.out.println(TestInitOrder.second);
  }

  public static void main(String args[]) {
  }

}

class Second {
  public Second() {
    System.out.println(TestInitOrder.second);
  }
}

根据Java语言规范4.12.5章节,程序中的每个变量在使用前必须具有值:
每个类变量、实例变量或数组组件在创建时都会初始化为默认值。
(规范中以下几行指定了所有类型的默认值,基本上是某种形式的0,例如00.0dnullfalse等。)
因此,在类被初始化之前(由于这些原因之一),变量将保持初始值。
根据详细的初始化过程(这里只引用了有趣的步骤,并加重了语气):
6. [...] 然后,初始化final类变量和接口字段,它们的值是编译时 常量表达式(§8.3.2.1, §9.3.1, §13.4.9, §15.28)。
[...]
9. 接下来,按照文本顺序执行类的类变量初始化器和静态初始化器,或者接口的字段初始化器,就好像它们是单个块一样。
让我们看看第6步,其中有4个final类变量:stat1str2secondlazy
由于10是常量表达式,"sdfff"也是,且由于执行顺序,无法观察str2stat1的初始值。要进行观察,最早可以在第9步进行。 second的情况表明,当右侧不是编译时常量表达式时,其初始值是可见的。 lazy的情况不同,因为赋值是在静态块中完成的,因此发生在第9步——因此可以观察到其初始值。(好吧,编译器仔细检查lazy只被分配一次)。

在使用编译时常量表达式初始化最终类变量之后,将执行静态块和其余的初始化器。

从示例中可以看出,静态块和初始化按照文本顺序进行 - 通过使用str变量进行演示 - 它首先被打印为null,然后是something,最后是crap


@EJP:请注意,2个条件1)final类变量和2)初始化程序是编译时常量,这在步骤6中完成。这里没有矛盾。您甚至可以在ideone上检查程序。 - nhahtdh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接