在Java中运行构造函数代码之前是否初始化字段?

在Java中运行构造函数代码之前是否初始化字段?,java,constructor,initialization,Java,Constructor,Initialization,有人能解释一下下面程序的输出吗?我认为构造函数是在实例变量之前初始化的。所以我希望输出是“xzy” 如果您查看类文件的反编译版本 class X { Y b; X() { b = new Y(); System.out.print("X"); } } class Y { Y() { System.out.print("Y"); } } public class Z extends X { Y

有人能解释一下下面程序的输出吗?我认为构造函数是在实例变量之前初始化的。所以我希望输出是“xzy”


如果您查看类文件的反编译版本

class X {
    Y b;

    X() {
        b = new Y();
        System.out.print("X");
    }
}

class Y {
    Y() {
        System.out.print("Y");
    }
}

public class Z extends X {

    Y y;

    Z() {
        y = new Y();
        System.out.print("Z");
    }

    public static void main(String args[]) {
        new Z();
    }
}
您可以发现实例变量
y
在构造函数中移动,因此执行顺序如下

  • 调用
    Z的构造函数
  • 它触发默认构造函数
    X
  • 调用
    X
    构造函数
    newy Y()
    的第一行
  • 打印Y
  • 打印X
  • 调用构造函数Z
    newy Y()中的第一行
  • 打印
    Y
  • 打印Z

  • 所有实例变量都是使用构造函数语句初始化的

    正确的初始化顺序是:

  • 静态变量初始化器和静态初始化块,如果类以前没有初始化过,则按文本顺序
  • 构造函数中的super()调用,无论是显式的还是隐式的
  • 实例变量初始化器和实例初始化块,按文本顺序排列
  • super()之后构造函数的剩余主体

  • 参见章节。

    初始化顺序在JLS 12.5中规定:

    1.首先,为新对象分配内存

    2.然后将对象中的所有实例变量(包括此类及其所有超类中定义的实例变量)初始化为其默认值

    3.最后,调用构造函数


    为了澄清对static的误解,我将简单地引用这段代码:

    public class Foo {
      { System.out.println("Instance Block 1"); }
      static { System.out.println("Static Block 1"); }
      public static final Foo FOO = new Foo();
      { System.out.println("Instance Block 2"); }
      static { System.out.println("Static Block 2 (Weird!!)"); }
      public Foo() { System.out.println("Constructor"); }
      static public void main(String p[]) {
        System.out.println("In Main");
        new Foo();
      }
    }
    
    令人惊讶的是,输出如下所示:

    静态块1
    实例块1
    实例块2
    建造师
    静态块2(奇怪!!)
    大体上
    实例块1
    实例块2
    建造师
    
    注意,我们有一个在两个实例
    {}
    之后调用的
    静态{}
    。这是因为我们在中间抛出构造函数,第一次调用构造函数时调用执行顺序。

    我在做这个答案的时候发现了这个-

    基本上,我们观察到这种情况:

  • 在第一次初始化对象时, 根据发生顺序混合初始化静态和实例的当前对象

  • 对于所有后续初始化,只按照发生的顺序执行实例初始化,因为静态初始化已经发生


  • 我需要研究继承的混合,以及对super的显式和隐式调用,这将如何影响这一点,并将随着研究结果而更新。这可能与其他提供的答案类似,只是他们在静态初始化时弄错了

    调用构造函数时,实例变量初始值设定项在构造函数主体之前运行。您认为下面程序的输出是什么

    public class Tester {
        private Tester internalInstance = new Tester();
        public Tester() throws Exception {
            throw new Exception("Boom");
        }
        public static void main(String[] args) {
            try {
                Tester b = new Tester();
                System.out.println("Eye-Opener!");
            } catch (Exception ex) {
                System.out.println("Exception catched");
            }
        }
    }
    
    main方法调用Tester构造函数,该构造函数引发异常。您可能希望catch子句捕获此异常并打印捕获的异常。 但是如果你试着运行它,你
    发现它没有这样做,并抛出了一个
    StackOverflowerError

    很好的解释@Arun p johny+1。实例变量的初始化被移动到构造函数中。至少没有对规范的另一个错误解释。它不完整,但有助于解释发生了什么。链接到JLS for Java 8:and同样是错误的——请看我对@ÓscarLópez的评论。请注意,
    static
    并不是您所指文档部分的一部分。带有注释的另一篇文章已被删除-但我在一个补充回答中提供了详细信息以及示例代码。不是一个完整的答案-但至少强调了
    静态{}
    案例。@YoYo这个答案与JLS一致:具体地说,关于您在答案中提到的执行,与第9项一致。如果您的实现不符合JLS,请将其报告为bug。混淆的是类和实例实例化是分开讨论的。我想这就留下了实现细节来说明两者如何混合;DR:类成员初始化中的new被隐式地放在构造函数的顶部。因此,您基本上是在试图显示构造函数将在控件到达抛出部分之前继续调用自己(因为行private Tester internalInstance=new Tester();)。我的理解正确吗?这是因为
    private static Foo instance=new Foo(),在第二个静态块之前运行。事实上,它是第二个静态块,您标记的第二个实际上是第三个。这里不神秘。
    
    public class Tester {
        private Tester internalInstance = new Tester();
        public Tester() throws Exception {
            throw new Exception("Boom");
        }
        public static void main(String[] args) {
            try {
                Tester b = new Tester();
                System.out.println("Eye-Opener!");
            } catch (Exception ex) {
                System.out.println("Exception catched");
            }
        }
    }