为什么我的实例初始化程序块在声明之前引用了一个字段?

时间:2018-01-11 21:10:19

标签: java instance-initializers

我的理解是你不能在声明它之前引用变量,并且所有代码(包括实例初始值设定项)都在一个类的主体内,但在任何方法之外,在构造函数之前按顺序执行创建对象(例外是static变量和初始化程序块,它们在程序开始时按顺序运行,以初始化整个类)。那么,为什么以下代码编译(并运行!):

public class WhyIsThisOk {
    { a = 5; } // why is this ok???
    int a = 10;

    public WhyIsThisOk() {
    }

    public static void main(String[] args) {
        WhyIsThisOk why = new WhyIsThisOk();
        System.out.println(why.a); // 10
    }
}

5 个答案:

答案 0 :(得分:4)

来自docs

  

Java编译器将初始化程序块复制到每个构造函数中。   因此,这种方法可用于共享一段代码   多个构造函数。

以上陈述略有误导,因为如果我们按照上述文档的说明,我们可以像这样重写原始代码:

public class WrongVersionOfWhyIsThisOk {

    int a = 10;

    public WhyIsThisOk (){
        a = 5;
    }

    public static void main(String[] args){
        WrongVersionOfWhyIsThisOk why = new WrongVersionOfWhyIsThisOk ();
        System.out.println(why.a);
    }
}

但是运行WrongVersionOfWhyIsThisOk将产生原始代码产生的5而不是10。

但实际上,初始化程序块和变量赋值都被复制到构造函数中:

public class RightVersionOfWhyIsThisOk {

    int a;

    public RightVersionOfWhyIsThisOk (){
        a = 5;
        a = 10;
    }

    public static void main(String[] args){
        RightVersionOfWhyIsThisOk why = new RightVersionOfWhyIsThisOk ();
        System.out.println(why.a);
    }
}

<强>更新

这是doc详细描述初始化顺序和构造函数调用:

  

4)执行实例初始值设定项和实例变量   此类的初始值设定项,分配实例变量的值   初始化器到相应的实例变量中   从左到右的顺序,它们在源代码中以文本形式出现   为了上课。如果执行任何这些初始化程序导致   异常,然后没有处理进一步的初始化程序   程序突然完成同样的异常。除此以外,   继续第5步。

     

5)执行此构造函数的其余部分。如果执行   突然完成,然后这个程序突然完成   同样的道理。否则,此过程正常完成。

答案 1 :(得分:1)

在实例创建时调用实例初始化块。所以在创建为什么对象之后,它是正常的,这是正常的。

初始化的顺序是: 1)静态集团 2)构造 3)出现顺序的实例集团

答案 2 :(得分:1)

只要调用任何构造函数(在构造函数的内容之前),就会执行初始化程序块的内容。

因此,您可以提供对任何变量的引用,因为除非调用构造函数,否则它们将不会被使用。对象已创建。

答案 3 :(得分:1)

来自docs

  

8.3.2.3。初始化期间使用字段的限制

     

成员的声明需要以文本形式出现   仅当成员是实例(分别是静态)字段时才使用   类或接口C以及以下所有条件:

     
      
  • 用法发生在C的实例(分别是静态)变量初始化器或实例(分别是静态)初始化器中   C。

  •   
  • 用法不在作业的左侧。

  •   
  • 用法是通过一个简单的名称。

  •   
  • C是封闭用法的最里面的类或接口。

  •   
     

如果上述四个要求中的任何一个是,则是编译时错误   不符合

在这种情况下,用法位于赋值的左侧,因此它不是编译时错误。

答案 4 :(得分:0)

声明的顺序并不重要。你也可以写:

public  class WhyIsThisOk {

    {
        a = 5;
    }

    public WhyIsThisOk() {
    }

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

    int a = 10;
}

重要的是,编译器首先将a=5然后a=10复制(自上而下)到构造函数中,所以它看起来像:

public WhyIsThisOk() {
    a = 5;
    a = 10;
}

最后看一下这个例子:

public class WhyIsThisOk {

    {
        a = get5();
    }

    public WhyIsThisOk() {
        a = get7();
    }

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

    int a = get10();

    public static int get5() {
        System.out.println("get5 from: " + new Exception().getStackTrace()[1].getMethodName());
        return 5;
    }

    public static int get7() {
        System.out.println("get7 from: " + new Exception().getStackTrace()[1].getMethodName());
        return 7;
    }

    public static int get10() {
        System.out.println("get10 from: " + new Exception().getStackTrace()[1].getMethodName());
        return 10;
    }
}

输出是:

get5 from: <init>
get10 from: <init>
get7 from: <init>
7