美文网首页
类加载过程

类加载过程

作者: gczxbb | 来源:发表于2019-07-23 16:15 被阅读0次

    一、加载过程

    类加载过程

    加载,连接(验证,准备,解析),初始化,使用,卸载。

    1,加载
    class字节码文件描述类的各种信息。根据类全限定名,将二进制字节流加载到内存,可以是class文件外的其他渠道(如网络、动态生成),在堆生成一个代表类的Class对象,访问方法区类型信息数据的入口。
    2,连接
    验证,文件格式,元数据,字节码,符号引用。
    准备,类变量分配内存,设置默认初始值0,(非final的static类型)。final类变量设置常量目标值。
    解析,常量池符号引用转为直接引用。
    3,初始化
    类变量初始化语句和静态代码块,编译时,放在收集器,在<clinit>方法,类加载过程由JVM调用。优先初始化父类,仅允许一个线程对类初始化,没有静态变量和代码块时,可以没有<clinit>方法。

    类信息
    完整有效名,修饰符,类型直接接口的有序列表和父类,常量池,Field域信息,Method方法信息,静态变量static,classloader引用。

    二、类初始化

    触发条件

    创建一个类的对象实例,new关键字、反射、序列化。
    读取一个类或接口public静态变量,(final除外)。
    调用一个类的静态方法。
    使用java.lang.reflect包的方法对类反射调用。
    初始化一个类的派生类时,先初始化该类。
    jvm启动包含main方法的启动类。

    public class Tea {
        public static int a  = 10 ;  
        public Tea(){
            System.out.println("构造方法代码");
        }
        static {              
            System.out.println("类初始化代码");
        }
        public static int b  = 100 ;    
        static {              
            System.out.println("类初始化代码2");
        }
    }
    

    Tea类,静态变量a和b赋值,静态代码块,反编译Tea结果。

    反编译Tea类

    编译器会自动搜集类变量的赋值动作与静态代码块语句,按照源文件出现的顺序,合并生成static{}方法。
    外部访问Tea.a变量时,Tea类初始化,按照出现的顺序赋值和执行。如果Tea有父类,父类初始化在子类前执行。
    Tea2类,继承Tea类。

    public class Tea2 extends Tea{
        public static int c  = 10 ;  
        public static final int d  = 10 ; 
    
        public Tea2(){
            System.out.println("构造方法代码");
        }
    
        static {              
            System.out.println("类初始化代码3");
        }
    }
    

    1,外部访问Tea2.a变量,仅Tea类初始化,Tea2不会初始化,子类引用父类静态变量,仅父类初始化,子类不会初始化。
    2,外部访问Tea2.c变量,Tea2类初始化,先初始化Tea类。
    3,外部访问Tea2.d变量,Tea2类不会初始化,final常量在编译阶段存储在类常量池,不会导致该类初始化。
    4,外部定义一个Tea2类型数组,Tea2类不会初始化。

    public class Tea3 {
        public static int e  = 10 ;  
        public static int b = 12;
        public static Tea3 f  = new Tea3() ;
        
        public Tea3(){
            System.out.println("构造方法代码:a="+a+" b="+b);
        }
    
        static { 
            b+=1;
            System.out.println("类初始化代码");
        }
       
        public int a = 11;  
        public static Tea3 g  = new Tea3() ;
    }
    

    外部引用Tea3.e变量,导致Tea3类初始化,流程:
    1,初始化e,b变量值,
    2,Tea3实例f,a实例变量初始化11,构造方法,打印a=11,b=12,
    3,初始化static{}代码,b值自增。
    4,初始化Tea3实例g,a实例变量初始化11,构造方法,打印a=11,b=13。

    因此,实例初始化不一定在类初始化结束才开始,类初始化时,遇到静态对象赋值,将会进行实例初始化。

    将实例化嵌入到类初始化流程中,导致实例化发生在某些类变量初始化之前。初始化本质是赋值,创建对象后,按顺序如果某个static变量还未初始化值就是0。

    三、总结

    1,实例初始化不一定在类初始化结束后开始。

    2,虚拟机保证类构造器方法的加锁和同步,在同一个类加载器,一个类型只会被初始化一次。

    3,如果没有类变量或静态代码块,可以不产生类构造器。

    4,子类引用父类静态变量,仅父类初始化,子类不会初始化。


    任重而道远

    相关文章

      网友评论

          本文标题:类加载过程

          本文链接:https://www.haomeiwen.com/subject/dllalctx.html