JAVA初始化块

时间:2013-12-10 06:44:33

标签: java

正如一些消息来源所说,Java实例初始化块在创建实例时或在构造函数之前执行。但想象一下这个案例:

public class Foo {

    {
        System.out.println("Foo init");
    }

    public Foo()
    {

        {
            System.out.println("Foo constr");
        }
    }
}

public class Main extends Foo {

    {
        System.out.println("Main init");
    }

    public Main()
    {

        {
            System.out.println("Main constr");
        }
    }

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

输出是(如预测的那样):

Foo init
Foo constr
Main init
Main constr

所以我的问题是 - 实例初始化块的正确定义是什么,因为它显然不是在构造函数之前执行,因为输出应该是

Main init
Foo init
Foo constr
Main constr

因为Main()构造函数在调用super()之前被调用,而Main初始化块应该是第一个。

6 个答案:

答案 0 :(得分:4)

没有。初始化块直接复制到构造函数中。显然,那里也有一个隐含的超级。所以你的例子变成了

public class Foo {
  public Foo()
  {
    {System.out.println("Foo init");} // initializer.
    {System.out.println("Foo constr");}
  }
}

public class Main extends Foo {
  public Main()
  {  
    super(); // super constructor.
    {System.out.println("Main init");} // initializer.
    {System.out.println("Main constr");}
  }
  public static void main(String[] args) {
    new Main();
  }
}

这解释了您观察到的行为

Foo init
Foo constr
Main init
Main constr

答案 1 :(得分:0)

        new Main(); 

上述状态将首先转到Main()。但是在Main()中执行任何操作之前,都会调用super()。所以,调用Foo()。现在,在Foo()中进行检查以查看实例初始化块。所以,你明白了。 “Foo init”。接下来执行Foo()中的语句。所以,你得到 - “Foo constr”。接下来,控件返回到Main(),Main()现在检查Main类是否存在初始化块。因此,打印“Main Init”..然后打印Main()的其他语句。定义是正确的..理解......好......取决于你怎么看......

答案 2 :(得分:0)

最后一个陈述不正确。在 Foo()之后执行Main(),因为类构造函数在其超级构造函数完成后执行。 有关更多详细信息,请参阅this part of the spec,,其中涉及隐式和显式超级构造函数调用。

答案 3 :(得分:0)

编译器将代码转换为如下代码:

public class Main extends Foo {
  void _init()
  {System.out.println("Main init");}

  public Main()
  {
    super();
    _init();
    {System.out.println("Main constr");}
  }

}

主要规则是:

  • 在当前类的任何初始化代码
  • 之前调用super()
  • 初始化程序块在构造函数体之前调用

答案 4 :(得分:0)

在JLS中有一个specific initialization procedure解释,但让我提炼出最关键的部分:

  • 初始化类的过程是依赖于实现的JVM。
  • 如果超类尚未初始化,则超类在其子类(步骤7)之前初始化。
  • 评估所有静态初始值设定项和字段,就好像它们是单个块的文本顺序。最后一部分很重要;它意味着首先初始化的东西首先被初始化。

这就是为什么你看到你所做的行为 - 因为MainFoo的子类,它还没有被初始化,所以那时它的静态块没有被评估。 / p>

因此,Main的构造函数直到Foo的构造函数之后才会执行,因为子类中有an implicit call to super()

答案 5 :(得分:0)

了解此代码在内部的工作原理:

class Foo {

    {System.out.println("Foo init");}

    public Foo()
    {

        {System.out.println("Foo constr");}
    }
}



class Main extends Foo {

    {System.out.println("Main init");}

    public Main()
    {

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

        new Main();
    }
}

步骤1:JVM调用main()

Main方法

步骤2:构造函数Main()具有内部super(),如果您不使用this(),则由JVM提供,因此super将调用超级构造函数,即超级Foo()类。

步骤3:现在在调用超类的Foo之前​​,JVM将检查是否有IIB即实例初始化块,因此,在打印“Foo constr”之前将打印“Foo init”

现在输出到现在是:

Foo init 
Foo constr

步骤4:我们的控制返回到当前构造函数并在执行之前再次执行当前的construstor JVM将调用IIB,即实例初始化块和“Main init”将被打印。然后最后“主要建设”

所以最后是:

Foo init
Foo constr
Main init
Main constr

实际上,任何构造函数的第一次调用始终是Super()this()。 你正在使用new Main();创建新对象,当然这将调用构造函数,总是调用构造函数来初始化对象。