【问题标题】:Why instance initiazer block in java executed only once?为什么java中的实例初始化块只执行一次?
【发布时间】:2014-03-20 00:34:22
【问题描述】:
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 tutorials

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

那么为什么 B 类的初始化块没有执行两次,而构造函数执行了两次呢?

【问题讨论】:

  • 那么你希望它执行多少次?初始化过程在 Universe 中的任何创建过程中只执行一次。
  • 请参阅javaworld.com/article/2076614/core-java/…,了解 Java 中对象初始化的更详细概述。

标签: java


【解决方案1】:

那么为什么 B 类的初始化块没有执行两次,而构造函数执行了两次呢?

不,构造函数只运行一次。编译器会考虑将工作委派给另一个构造函数,并且不会将实例初始化程序复制到以 this() 调用开头的构造函数中。

一般而言,您无需费心去推理实例初始化程序代码的确切复制位置。只需依赖于它会为每个对象初始化运行一次且仅一次这一事实。

实例初始化程序运行的时刻是在完成super() 构造函数调用之后。


术语注释

您在问题中包含的链接不是 Javadocs,而是 Oracle 教程。这两者之间有一个非常重要的区别:Javadocs 是规范的,而教程只是描述性的。作为教学价值和事实准确性之间的折衷,教程中的某些措辞可能不够精确。

如果您对在教程中阅读的内容有任何疑问,请尝试在 Java 语言规范或 JDK Javadoc 中查找权威声明。

【讨论】:

  • 因此语句“Java 编译器将初始化程序块复制到每个构造函数中。”是假的???
【解决方案2】:

您只创建了一个B.i.e.的实例。 C 的一个实例。所以,它只会被打印一次,因为构造函数只运行一次。尝试创建另一个C 实例,然后您将打印两次。

【讨论】:

    【解决方案3】:
    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        
    }
    

    您会看到初始化程序块实际上并未复制到所有构造函数。它仅在布尔构造函数中复制,因为编译器检测到无参数构造函数委托给布尔构造函数。因此,教程中的句子是对实际发生的事情的简化。

    【讨论】:

    • 你能添加一个关于这个规范的链接吗?
    猜你喜欢
    • 1970-01-01
    • 2015-09-15
    • 1970-01-01
    • 1970-01-01
    • 2014-09-14
    • 1970-01-01
    • 2011-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多