【问题标题】:Why do fields seem to be initialized before constructor?为什么字段似乎在构造函数之前被初始化?
【发布时间】:2015-02-08 06:57:07
【问题描述】:
public class Dog {

 public static Dog dog = new Dog();
 static final int val1 = -5;
 static int val2 = 3;
 public int val3;

 public Dog() {
      val3 = val1 + val2;
 }

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

输出为-5

从这个结果来看,val2 的初始化似乎是在dog 成员及其实例化完成之前。

为什么这个顺序是这样的?

【问题讨论】:

  • 可能是先声明最终字段(可能是因为它们在 java 字节码中的存储方式不同),然后按照您编写它们的顺序声明非最终字段。如果这是正确的,那么在声明 Dog 实例时,val1 将为 -5,但 val2 仍设置为默认值 0。
  • 这是因为在类加载时会先加载final变量,然后再按声明的顺序加载剩余的静态变量。有关更多说明,请参阅stackoverflow.com/questions/12448465/…
  • final int 使 int 成为 compile-time 常量。

标签: java output


【解决方案1】:

如果你最后移动你的狗实例,你可能会发现输出变成-2

public class Dog {

     static final int val1 = -5;// This is final, so will be initialized at compile time 
     static int val2 = 3;
     public int val3;

     public static Dog dog = new Dog();//move to here
 
     public Dog() {
          val3 = val1 + val2;
     }

    public static void main(String[] args) {
        System.out.println(Dog.dog.val3);//output will be -2
    }
}

最终的字段(其值为编译时常量表达式)将首先被初始化,然后其余的将按文本顺序执行。

因此,在您初始化狗实例的情况下,static int val2(0) 尚未初始化,而 static final int val1(-5) 会因为它是最终的。

http://docs.oracle.com/javase/specs/jls/se5.0/html/execution.html#12.4.2 声明:

执行类变量初始化器和静态 类的初始值设定项,或接口的字段初始值设定项, 按文字顺序,就好像它们是一个单独的块一样,除了 final 类变量和接口的字段,其值为 首先初始化编译时常量


更新了一个较新的文档

这是来自http://docs.oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.4.2的jdk7版本

final 字段在第 6 步:

然后,初始化最终的类变量和接口的字段 其值为编译时常量表达式

当静态字段在第 9 步时:

接下来,执行类变量初始化器和静态 类的初始值设定项,或接口的字段初始值设定项, 按照文字顺序,就好像它们是一个单独的块一样。

【讨论】:

  • 谢谢,你给了官方参考,这是我所期望的。
【解决方案2】:

变量声明顺序。 static final int val1 首先被初始化,因为它是一个常量。然而static int val2public static Dog dog = new Dog(); 被实例化时仍然是0

【讨论】:

    【解决方案3】:

    发生了什么事..

    正在执行的第一行是 public static Dog dog = new Dog();。 现在,有两件事必须牢记。

    1. final int 使其成为编译 时间常数。因此,-5 已经硬编码到您的代码中。

    2. 对 new Dog() 的调用完成并调用构造函数,将值设置为 0 + -5 = -5

    val2 更改为final 然后你会看到不同(你会得到-2 作为答案。)

    注意:静态字段被初始化为以及如何遇到它们。

    【讨论】:

      【解决方案4】:

      测试中的初始化序列;

      1. static final int val1 = -5; //constant as static final
      2. public static Dog dog = new Dog(); //那么'dog'被初始化但是它的成员val2还没有被初始化
      3. static int val2 = 3; //最后'val2'被初始化了

      此代码更改将输出-2

      public class Dog {
      
           //public static Dog dog = new Dog();
           static final int val1 = -5;
           static int val2 = 3;
           public int val3;
           public static Dog dog = new Dog();     //moved here
      
           public Dog() {
                val3 = val1 + val2;
           }
      
      
          public static void main(String[] args) {
              System.out.println(Dog.dog.val3);
      
          }
      }
      

      【讨论】:

        【解决方案5】:

        所有静态变量都在单独的静态构造函数中初始化,该构造函数在类加​​载时执行。与它们在代码中出现的顺序相同。你的例子被编译成这样的:

        public class Dog {
        
         public static Dog dog;
         static final int val1 = -5;
         static int val2;
         public int val3;
        
         static {
          dog = new Dog();
          val2 = 3;
         }
        
         public Dog() {
              val3 = val1 + val2;
         }
        
         public static void main(String[] args) {
             System.out.println(Dog.dog.val3);
         }
        }
        

        这就是类/实例变量的顺序很重要的原因。类构造函数执行发生在初始化结束时。常量之前已解决。更多信息请参考Creation of New Class Instances

        【讨论】:

        • 你的例子很好地解释了我的程序,但我想知道你的论点是否有官方解释“所有静态变量都在单独的静态构造函数中初始化..”
        猜你喜欢
        • 1970-01-01
        • 2012-01-07
        • 1970-01-01
        • 1970-01-01
        • 2015-09-15
        • 1970-01-01
        • 1970-01-01
        • 2011-03-28
        • 1970-01-01
        相关资源
        最近更新 更多