【java基础☞初始化顺序】java继承中的初始化顺序

一、初始化顺序:父类的静态变量-->父类的静态代码块-->子类的静态变量-->子类的静态代码快-->父类的非静态变量(父类的非静态代码块)-->父类的构造函数-->子类的非静态变量(子类的非静态代码块)-->子类的构造函数java

重点关注:父类的非静态变量(父类的非静态代码块)是同一级的,看哪一个部分写在类的最前面谁就先被执行,子类的非静态变量(子类的非静态代码块)也是同样。

package com.test;


public class Test {
    public static void main(String[] args) {
        Child c=new Child();
    }
}
class Parent {
    public static PrintMessage a=new PrintMessage("父类静态成员被初始化");
    private PrintMessage b=new PrintMessage("父类非静态成员被初始化"); 
    static{
        System.out.println("父类的静态代码块被执行");
    }
    {
        System.out.println("父类的非静态代码块被执行");
    }
    public Parent(){
        System.out.println("父类的构造方法被执行");
    }   
}

class Child extends Parent{
    public static PrintMessage a1=new PrintMessage("子类静态成员被初始化");
    private PrintMessage b1=new PrintMessage("子类非静态成员被初始化");    
    
    static {
        System.out.println("子类的静态代码块被执行");
    }
    {
        System.out.println("子类的非静态代码块被执行");
    }
    public Child(){
        System.out.println("子类的构造函数被执行");
    }
}

class PrintMessage{
    public PrintMessage(String mes){
        System.out.println(mes);
    }
}

执行结果:数据结构

父类静态成员被初始化
父类的静态代码块被执行
子类静态成员被初始化
子类的静态代码块被执行
父类非静态成员被初始化
父类的非静态代码块被执行
父类的构造方法被执行
子类非静态成员被初始化
子类的非静态代码块被执行
子类的构造函数被执行函数

 

二、JAVA 类的加载机制spa

Java类加载分为5个过程,分别为:加载,链接(验证,准备,解析),初始化,使用,卸载。设计

1. 加载
    加载主要是将.class文件(也能够是zip包)经过二进制字节流读入到JVM中。 在加载阶段,JVM须要完成3件事:
    1)经过classloader在classpath中获取XXX.class文件,将其以二进制流的形式读入内存。
    2)将字节流所表明的静态存储结构转化为方法区的运行时数据结构;
    3)在内存中生成一个该类的java.lang.Class对象,做为方法区这个类的各类数据的访问入口。code

2.链接对象

    2.1. 验证
    主要确保加载进来的字节流符合JVM规范。验证阶段会完成如下4个阶段的检验动做:
    1)文件格式验证
    2)元数据验证(是否符合Java语言规范)
    3)字节码验证(肯定程序语义合法,符合逻辑)
    4)符号引用验证(确保下一步的解析能正常执行)
    2.2. 准备
    准备是链接阶段的第二步,主要为静态变量在方法区分配内存,并设置默认初始值。
    2.3. 解析
    解析是链接阶段的第三步,是虚拟机将常量池内的符号引用替换为直接引用的过程。继承

3. 初始化ip

    初始化阶段是类加载过程的最后一步,主要是根据程序中的赋值语句主动为类变量赋值。
    当有继承关系时,先初始化父类再初始化子类,因此建立一个子类时其实内存中存在两个对象实例。
 注:若是类的继承关系过长,单从类初始化角度考虑,这种设计不太可取。缘由我想你已经猜到了。
    一般建议的类继承关系最多不超过三层,即父-子-孙。某些特殊的应用场景中可能会加到4层,但就此打住,第4层已经有代码设计上的弊端了。
内存

4. 使用
    程序之间的相互调用。

5. 卸载
    即销毁一个对象,通常状况下中有JVM垃圾回收器完成。代码层面的销毁只是将引用置为null。

经过上面的总体介绍后,再来看Singleton2.getInstance()的执行分析:
    1)类的加载。运行Singleton2.getInstance(),JVM在首次并无发现Singleton类的相关信息。因此经过classloader将Singleton.class文件加载到内存中。
    2)类的验证。略
    3)类的准备。将Singleton2中的静态资源转化到方法区。value1,value2,singleton在方法区被声明分别初始为0,0,null。
    4)类的解析。略(将常量池内的符号引用替换为直接引用的过程)
    5)类的初始化。执行静态属性的赋值操做。按照顺序先是value1 = 5,value2 = 3,接下来是private static Singleton2 singleton2 = new Singleton2();
这是个建立对象操做,根据 结论1 在执行Singleton2的构造方法以前,先去执行static资源和非static资源。但因为value1,value2已经被初始化过,因此接下来执行的是非static的资源,最后是Singleton2的构造方法:value1++;value2++。
因此Singleton2结果是6和4。

以上除了搞清楚执行顺序外,还有一个重点->结论2:静态资源在类的初始化中只会执行一次。不要与第3个步骤混淆。

有了以上的这个结论,再来看Singleton.getInstance()的执行分析:
    1)类的加载。将Singleton类加载到内存中。
    2)类的验证。略
    3)类的准备。将Singleton2的静态资源转化到方法区。
    4)类的解析。略(将常量池内的符号引用替换为直接引用的过程)
    5)类的初始化。执行静态属性的赋值操做。按照顺序先是private static Singleton singleton = new Singleton(),根据 结论1 和结论2,value1和value2不会在此层执行赋值操做。因此singleton对象中的value1,value2只是在0的基础上进行了++操做。此时singleton对象中的value1=1,value2=1。 而后, public static int value1 = 5; public static int value2 = 3; 这两行代码才是真的执行了赋值操做。因此最后的结果:5和3。 若是执行的是public static int value1; public static int value2;结果又会是多少?结果: 1和1。

相关文章
相关标签/搜索