zoukankan      html  css  js  c++  java
  • Class加载顺序

    原文:https://blog.saymagic.cn/2017/07/01/class-common-question.html

    类的初始化顺序是怎样的?

    我们尝试从class文件中找到答案。来看这样的一段代码:

    public class InitialOrderTest {
        public static String staticField = "   StaticField";
        public String fieldFromMethod = getStrFromMethod();
        public String fieldFromInit = "   InitField";
        static {
            System.out.println( "Call Init Static Code" );
            System.out.println( staticField );
        }
        {
            System.out.println( "Call Init Block Code" );
            System.out.println( fieldFromInit );
            System.out.println( fieldFromMethod );
        }
        public InitialOrderTest()
        {
            System.out.println( "Call Constructor" );
        }
        public String getStrFromMethod(){
            System.out.println("Call getStrFromMethod Method");
            return "   MethodField" ;
        }
        public static void main( String[] args )
        {
            new InitialOrderTest();
        }
    }
    
    

    结果:
    在这里插入图片描述

    我们来一一来看一下它的class文件中的内容,首先是有一个static方法区:

    static {};
        descriptor: ()V
        flags: ACC_STATIC
        Code:
          stack=2, locals=0, args_size=0
             0: ldc           #14                 // String    StaticField
             2: putstatic     #15                 // Field staticField:Ljava/lang/String;
             5: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;
             8: ldc           #16                 // String Call Init Static Code
            10: invokevirtual #8                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
            13: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;
            16: getstatic     #15                 // Field staticField:Ljava/lang/String;
            19: invokevirtual #8                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
            22: return
    

    Java编译器在编译阶段会将所有static的代码块收集到一起,形成一个特殊的方法,这个方法的名字叫做clinit, 这个名字容易让我们联想到构造函数的名称叫做init,但与构造函数不同,这个方法在Java层中是调用不到的,并且,这个函数是在这个类被加载时,由虚拟机进行调用。注意的是,是类被加载,而不是类被初始化成实例。所以,静态代码块的加载优先于普通的代码块,也优先于构造函数。这属于虚拟机规定的范畴,我们不做更深入的探讨。

    在Class文件中,是没有为普通方法区开辟类似于clinit这种方法的,而是将所有普通方法区的代码都合并到了构造函数中,我们直接来看构造函数:

    public InitialOrderTest();
        descriptor: ()V
        flags: ACC_PUBLIC
        Code:
          stack=2, locals=1, args_size=1
             0: aload_0
             1: invokespecial #1                  // Method java/lang/Object."<init>":()V
             4: aload_0
             5: aload_0
             6: invokevirtual #2                  // Method getStr:()Ljava/lang/String;
             9: putfield      #3                  // Field field:Ljava/lang/String;
            12: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
            15: aload_0
            16: getfield      #3                  // Field field:Ljava/lang/String;
            19: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
            22: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
            25: ldc           #6                  // String Init Block
            27: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
            30: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
            33: ldc           #7                  // String Constructor
            35: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
            38: return
    

    通过分析构造函数,我们就可以对一个实例初始化的顺序一清二楚,首先,0,1在构造函数中调用了父类的构造函数,接着,4、5、6、9为成员变量进行赋值,25、27在执行实例的代码块,最后,33、35才是执行我们Java文件中编写的构造函数的代码。这样,一个普通类的初始化顺序大致如下:

    静态代码按照顺序初始化 -> 父类构造函数 -> 变量初始化 -> 实例代码块 -> 自身构造函数

  • 相关阅读:
    SVGEditor
    SVG六基本元素
    SVG在网页中的四种使用方式
    Chrome中java因过期而遭到阻止
    Weblogic常见故障常:JDBC Connection Pools
    MyBatis java.sql.SQLSyntaxErrorException: ORA-00911: 无效字符
    MYBATIS 无效的列类型: 1111
    [MyBatis]mapperLocations属性通配符的使用
    secureCRT自动化脚本
    google protocol buffer的原理和使用(四)
  • 原文地址:https://www.cnblogs.com/sunqiangstyle/p/10312263.html
Copyright © 2011-2022 走看看