【问题标题】:static block vs initializer block vs constructor in inheritance [duplicate]继承中的静态块与初始化块与构造函数[重复]
【发布时间】:2014-02-25 14:28:30
【问题描述】:

我找到了这个例子,我想了解它背后的逻辑?构造函数和静态块以及初始化块如何在继承中工作?分别在哪个阶段被调用?

public class Parent {

    static {
        System.out.println("i am Parent 3");
    }

    {
        System.out.println("i am parent 2");
    }

    public Parent() {
        System.out.println("i am parent 1");
    }

}

public class Son extends Parent {

    static {System.out.println("i am son 3");}
    {System.out.println("i am son 2");}

    public Son() {
        System.out.println("i am son 1");
    }

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

输出是:

i am Parent 3
i am son 3
i am parent 2
i am parent 1
i am son 2
i am son 1

【问题讨论】:

  • 您已经提交了自己问题的答案。
  • 不要使用数字,而是使用专有名称,如静态块、初始化块、构造函数,以使输出更清晰。

标签: java inheritance constructor polymorphism static-block


【解决方案1】:

你需要知道

  1. 构造函数中的第一条指令是调用其父类super(params) 的构造函数,或者如果您想使用默认构造函数super()。如果是默认构造函数,您不必显式编写它。
  2. 初始化块中的代码在 super(...) 调用之后立即移动到每个构造函数
  3. 静态块在类初始化时执行,这是在 JVM 完全加载(及其父类)后完成的。

所以类被编译成类似这样的类。

public class Parent {
    static {
        System.out.println("Parent static block");
    }

    public Parent() {
        super();
        {
            System.out.println("Parent initializer block");
        }
        System.out.println("Parent constructor");
    }

}

public class Son extends Parent {

    static {
        System.out.println("Son static block");
    }

    public Son() {
        super();
        {
            System.out.println("Son initializer block");
        }
        System.out.println("Son constructor");
    }

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

为了能够从Son 类执行main 方法,JVM 需要加载这个类(以及它扩展的类)的代码。类完全加载后,JVM 初始化它的静态内容涉及执行静态块(是的,一个类中可以有多个静态块)。要完全加载Son 类,JVM 需要了解其父类的详细信息,因此它将在Son 之前完全加载Parent 类,这意味着它还将在Son 类中的静态块之前执行其静态块。

所以输出看起来像:

  • Parent static block
  • Son static block

现在在main 方法中,您通过new Son() 调用Son 类构造函数,代码如下所示

super();
{
    System.out.println("Son initializer block");
}
System.out.println("Son constructor");

由于其super()引用Parent类构造函数,即

super();// this will invoke Object constructor since Parent 
        // doesn't extend anything (which means it extends Object class)
{
    System.out.println("Parent initializer block");
}
System.out.println("Parent constructor");

结果你会看到

  • Parent initializer block
  • Parent constructor

这会处理Parent#constructor()super() 一起执行,所以接下来你会看到super() 之后来自 Son 构造函数的代码,它将生成

  • Son initializer block
  • Son constructor

要查看在您使用 Son 构造函数甚至 main 方法之前会加载类,您可以在使用 Son 构造函数之前打印一些内容,例如

System.out.println("ABC                      // before new Son()");
new Son();

这将导致

Parent static block
Son static block
ABC                      // before new Son()
Parent initializer block
Parent constructor
Son initializer block
Son constructor

【讨论】:

  • 没有关于它的“在此期间”。静态初始化程序首先被调用,并且在第一次加载和初始化类时调用一次,这可能是在调用任何构造函数之前的数小时或数周。你还没有说清楚。
  • @EJP 你是对的,删除它。
  • 我不只是评论这三个词。您根本没有将类加载和初始化与构造分开。
  • 注意Son应该被JVM加载来调用main
  • @SotiriosDelimanolis 没错。与建筑无关。
【解决方案2】:

当你的类被加载并且你的类首先由 jvm 中的类加载器加载时调用的静态块,所以首先它们会被执行

然后你创建对象,然后调用你的父初始化块,然后调用你的父构造函数,因为 java 中的构造函数链接,然后是派生类初始化块,然后是派生类构造函数

【讨论】:

    【解决方案3】:

    静态块在类加载到 JVM 时执行,而构造函数块在创建实例时执行。

    静态初始化器相当于静态上下文中的构造器。你肯定会比实例初始化器更频繁地看到它。

    【讨论】:

      【解决方案4】:

      当类被JVM加载和初始化时,一个静态块被调用一次。实例初始化器在构造类的实例时执行,就像构造函数一样。

      静态和实例初始化器在Java Language Specification中描述

      【讨论】:

      • 而且,显然它是在构造函数之前调用的。
      • @anorton 一个类必须先初始化,然后才能实例化。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-09-15
      • 2011-02-15
      • 2014-03-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-03-21
      相关资源
      最近更新 更多