Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/369.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java初始化和实例化的顺序_Java - Fatal编程技术网

Java初始化和实例化的顺序

Java初始化和实例化的顺序,java,Java,我试图在JVM中拼凑初始化和实例化的过程,但JLS在一些细节上有点迟钝,因此如果有人介意澄清一些细节,我们将不胜感激。这就是我到目前为止所能理解的 初始化 递归初始化类的静态最终变量及其作为编译时常量的接口 退出递归,按文本顺序处理静态块和静态字段 实例化 递归初始化类的最终实例变量,这些变量是编译时常量 退出递归,以文本顺序处理非静态块和实例字段,并在返回时将它们预先发送给构造函数 好的,现在开始提问 接口是否按声明的顺序处理 接口是否在单独的递归堆栈中处理 a如果是,接口是在超类之前还是之后

我试图在JVM中拼凑初始化和实例化的过程,但JLS在一些细节上有点迟钝,因此如果有人介意澄清一些细节,我们将不胜感激。这就是我到目前为止所能理解的

初始化

递归初始化类的静态最终变量及其作为编译时常量的接口

退出递归,按文本顺序处理静态块和静态字段

实例化

递归初始化类的最终实例变量,这些变量是编译时常量

退出递归,以文本顺序处理非静态块和实例字段,并在返回时将它们预先发送给构造函数

好的,现在开始提问

接口是否按声明的顺序处理

接口是否在单独的递归堆栈中处理

a如果是,接口是在超类之前还是之后处理的

b如果是,那么我推断一个或其他接口或超类在其他编译时常量之前初始化了其非编译时常量字段是否正确

对非默认超级构造函数的调用在此过程中扮演什么角色

我的任何结论都错了吗

我是否遗漏了其他关键细节


区分类的初始化和对象的初始化很重要

类初始化

通过分配编译时常量字段,然后递归初始化超类(如果尚未初始化),然后处理静态初始值设定项(包括非编译时常量的静态字段的初始值设定项),初始化类或接口

正如您所注意到的,类的初始化本身并不会触发它实现的接口的初始化。因此,当第一次访问接口时,通常通过读取非编译时常量的字段来初始化接口。此访问可能在初始值设定项求值期间发生,从而导致递归初始化

还值得注意的是,访问编译时常量字段不会触发初始化,因为这些字段在以下位置进行计算:

对常量变量§4.12.4字段的引用必须在编译时解析为常量变量初始值设定项表示的值V

如果这样的字段是静态的,那么二进制文件中的代码中不应该存在对该字段的引用,包括声明该字段的类或接口。此类字段必须始终显示为已初始化§12.4.2;如果字段的默认初始值与V不同,则绝对不能遵守该值

如果这样的字段是非静态的,则二进制文件中的代码中不应存在对该字段的引用,包含该字段的类除外。它将是一个类而不是一个接口,因为接口只有静态字段。在创建实例§12.5期间,该类应具有将字段值设置为V的代码

对象初始化

通常通过计算类实例创建表达式来初始化对象。其进展如下:

为此构造函数调用将构造函数的参数分配给新创建的参数变量

如果此构造函数以使用此函数的同一类中另一个构造函数的显式构造函数调用§8.8.7.1开始,则使用相同的五个步骤递归地评估该构造函数调用的参数和过程。如果构造函数调用突然完成,那么这个过程也会因为同样的原因突然完成;否则,继续执行步骤5

此构造函数不会以使用此函数的同一类中的另一个构造函数的显式构造函数调用开始。如果此构造函数用于对象以外的类,则此构造函数将以使用super的超类构造函数的显式或隐式调用开始。使用相同的五个步骤递归地评估超类构造函数调用的参数和过程。如果构造函数调用突然完成,那么此过程也会因为同样的原因突然完成。否则,继续执行步骤4

执行该类的实例初始值设定项和实例变量初始值设定项,将实例变量初始值设定项的值按从左到右的顺序分配给相应的实例变量,它们在类的源代码中以文本形式出现。如果执行这些初始值设定项中的任何一个会导致异常,则不会再处理其他初始值设定项,并且此过程会在该异常的情况下突然完成。否则,继续执行步骤5

执行此构造函数主体的其余部分。如果执行突然完成,那么 s程序出于同样的原因突然完成。否则,此过程将正常完成


正如我们在步骤3中看到的,对超级构造函数的显式调用只会改变调用的超级类构造函数。

下面是一个在对象创建过程中打印每个步骤顺序的示例

InstanceCreateStepTest.java:

执行:

只需调用main方法,然后检查输出

小贴士:

由@PostConstruct标记的方法将不会被调用,除非您在某些容器(如Spring boot)内调用它,因为它依赖于这些容器来实现@PostConstruct之类的注释。
接口没有任何要初始化的内容。除非您使用该词的方式与我以前使用的方式不同。@BobDalgleish接口可能具有公共静态final字段。如果这些字段是具有初始值设定项的对象,例如公共静态最终ArrayList SomeString=new ArrayList;那么这就是初始化的一部分。
import javax.annotation.PostConstruct;

/**
 * Test steps of instance creation.
 * 
 * @author eric
 * @date Jan 7, 2018 3:31:12 AM
 */
public class InstanceCreateStepTest {
    public static void main(String[] args) {
        new Sub().hello();
        System.out.printf("%s\n", "------------");
        new Sub().hello();
    }
}

class Base {
    static {
        System.out.printf("%s - %s - %s\n", "base", "static", "block");
    }
    {
        System.out.printf("%s - %s - %s\n", "base", "instance", "block");
    }

    public Base() {
        System.out.printf("%s - %s\n", "base", "constructor");
    }

    @PostConstruct
    public void init() {
        System.out.printf("%s - %s\n", "base", "PostConstruct");
    }

    public void hello() {
        System.out.printf("%s - %s\n", "base", "method");
    }
}

class Sub extends Base {
    static {
        System.out.printf("%s - %s - %s\n", "sub", "static", "block");
    }
    {
        System.out.printf("%s - %s - %s\n", "sub", "instance", "block");
    }

    public Sub() {
        System.out.printf("%s - %s\n", "sub", "constructor");
    }

    @PostConstruct
    public void init() {
        System.out.printf("%s - %s\n", "sub", "PostConstruct");
    }

    @Override
    public void hello() {
        // super.hello();
        System.out.printf("%s - %s\n", "sub", "method");
    }
}