为什么Java中的实例初始化块只执行一次?

5
class B {

    {
        System.out.println("IIB B");
    }

    B(int i) {
        System.out.println("Cons B int");

    }

    public B() {
        this(10);
        System.out.println("Cons B");
    }
}

public class C extends B {

    {
        System.out.println("IIB C");
    }

    public C() {
        System.out.println("Cons C");
    }

    public static void main(String[] args) {
        C c1 = new C();
    }
}

输出

 IIB B
Cons B int
Cons B
 IIB C
Cons C

根据Oracle教程,Java编译器会将初始化块复制到每个构造函数中。因此,这种方法可用于在多个构造函数之间共享代码块。
那么为什么B类的初始化块不会随着构造函数执行两次而执行两次呢?

1
那么你想让它执行多少次?初始化过程在宇宙中的任何创建过程中只执行一次。 - Sachin Verma
请参阅 http://www.javaworld.com/article/2076614/core-java/object-initialization-in-java.html 了解更详细的Java对象初始化概述。 - Mark
3个回答

7
为什么B类的初始化块没有执行两次,而构造函数执行了两次?
不,构造函数只运行一次。将工作委托给另一个构造函数是由编译器考虑的,实例初始化块不会复制到以this()调用开头的构造函数中。
一般来说,你不需要去思考实例初始化块代码复制的具体位置。只需依赖于每个对象初始化时它将运行一次且仅运行一次这一事实即可。
实例初始化器运行的时刻是在完成super()构造函数调用后立即。
一个术语说明
你在问题中包含的链接不是Javadoc,而是Oracle教程。这两者之间有非常重要的区别:Javadoc是规范性的,而教程只是描述性的。教程中的某些措辞可能是不精确的,因为要在传授价值和事实准确性之间做出妥协。
如果你对教程中读到的某些内容有疑问,那么请尝试在Java语言规范或JDK Javadoc中找到权威的声明。

那么语句“Java编译器将初始化块复制到每个构造函数中。”是错误的吗? - Ravi Godara

4
您只创建了一个实例B,也就是C的一个实例。因此,构造函数仅运行一次,因此它将仅打印一次。尝试创建另一个C的实例,然后您将打印两次。

2
public class Initializer {
    {
        System.out.println("in initializer");
    }

    public Initializer() {
        this(false);
        System.out.println("in no-arg constructor");
    }

    public Initializer(boolean b) {
        System.out.println("in boolean constructor");
    }
}

这是上述类生成的字节码:

public class com.foo.Initializer {
  public com.foo.Initializer();
    Code:
       0: aload_0       
       1: iconst_0      
       2: invokespecial #1                  // Method "<init>":(Z)V
       5: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
       8: ldc           #3                  // String in no-arg constructor
      10: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      13: return        

  public com.foo.Initializer(boolean);
    Code:
       0: aload_0       
       1: invokespecial #5                  // Method java/lang/Object."<init>":()V
       4: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
       7: ldc           #6                  // String in initializer
       9: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      12: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
      15: ldc           #7                  // String in boolean constructor
      17: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      20: return        
}

您可以看到,初始化块并没有被复制到所有构造函数中。它只在布尔构造函数中被复制,因为编译器检测到无参构造函数委托给了布尔构造函数。因此,教程中的这句话实际上是对发生情况的简化。


你能否添加一个关于这个规范的链接? - n611x007

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