Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/398.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) 如果是,那么我推断一个或多个(接口或超类)的非编译时常量字段在其他编译时常量之前初始化是否正确

  • 对非默认super()构造函数的调用在此过程中扮演什么角色

  • 我的任何结论都错了吗

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


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

    类初始化

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

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

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

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

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

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

    对象初始化

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

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

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

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

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

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


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

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

    InstanceCreateStepTest.java:

    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");
        }
    }
    
    执行:

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

    提示:

    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");
        }
    }
    
    • @PostConstruct
      标记的方法不会被调用,除非您在某个容器内调用它,比如
      Spring boot
      ,因为它依赖于那些容器来实现像
      @PostCo>这样的注释