一文看懂静态初始化块、静态成员、初始化块、构造函数执行顺序以及用途

2,165 阅读3分钟

Tips

  • 非静态初始化块基本和构造函数一个作用,可以避免构造函数的代码重复。初始化块在类的每次构造都会执行
  • 静态初始化块只在类加载执行一次

一个有意思的盲区:

执行psvm的时候,类的构造函数并不会执行,也就是说这时候类的实例并不存在。

public static void main(String[] args) {}

测试执行顺序

C extens B,B extends A , Main类中 new C()

public class InitLearn {

    static class A{
        static Print  pA1 =new Print("static pA1");
        static {
            System.out.println("Static Initialization Block A");
        }
        static Print  pA2 =new Print("static pA2");
        {
            System.out.println("Initialization Block A Before Construct");
        }
        public A(){
            System.out.println("Construct A");
        }
        {
            System.out.println("Initialization Block A After Construct");
        }


    };
    static class B extends A{
        static Print  pB1 =new Print("static pB1");
        static Print  pB2 =new Print("static pB2");
        static {
            System.out.println("Static Initialization Block B");
        }
        {
            System.out.println("Initialization Block B Before Construct");
        }
        public B(){
            System.out.println("Construct B");
        }
        {
            System.out.println("Initialization Block B After Construct");
        }
    };
    static class C extends B{
        static Print  pC1 =new Print("static pC1");
        static {
            System.out.println("Static Initialization Block C");
        }
        static Print  pC2 =new Print("static pC2");
        {
            System.out.println("Initialization Block C Before Construct");
        }
        public C(){
            System.out.println("Construct C");
        }
        {
            System.out.println("Initialization Block C After Construct");
        }
    };
    static class Print{
        public Print(String v){
                System.out.println(v);

        }
    }

    static {
        System.out.println("static{} of the class run psvm to test");
    }
    public static void main(String[] args) {
          new C();
          //new C();
    }
}

输出:


static{} of the class run psvm to test //测试类的静态初始化块,在调用psvmstatic pA1 //A类静态成员 定义在静态初始化块前
Static Initialization Block A //A类静态初始化块
static pA2 //A类静态成员 定义在静态初始化块后
static pB1 //B类静态成员 定义在静态初始化块前
static pB2 //B类静态成员 定义在静态初始化块前
Static Initialization Block B //B类静态初始化块
static pC1  //C类静态成员 定义在静态初始化块前
Static Initialization Block C //C类静态初始化块
static pC2  //C类静态成员 定义在静态初始化块后
Initialization Block A Before Construct //A类初始化块
Initialization Block A After Construct  //A类初始化块
Construct A  //A类构造方法
Initialization Block B Before Construct 
Initialization Block B After Construct
Construct B
Initialization Block C Before Construct
Initialization Block C After Construct
Construct C
    
    

有趣的现象

  • InitLearn类是肯定没有被实例化过的,但是由于执行main入口函数用到了InitLearn类,于是static初始化块也被执行了;

  • 所有的静态初始化块都优先执行,其次才是非静态的初始化块和构造函数,它们的执行顺序是:

    1. 父类的静态初始化块/静态成员,顺序是声明顺序
    2. 子类的静态初始化块/静态成员,顺序是声明顺序
    3. 父类的初始化块(无论定义在构造方法前还是后)
    4. 父类的构造函数
    5. 子类的初始化块(无论定义在构造方法前还是后)
    6. 子类的构造函数
  • 如果之后再new 一个B(),只会出现以下日志,静态的初始化过程只执行一次

    Initialization Block A Before Construct
    Initialization Block A After Construct
    Construct A
    Initialization Block B Before Construct
    Initialization Block B After Construct
    Construct B
    Initialization Block C Before Construct
    Initialization Block C After Construct
    Construct C
    

总结

  1. 静态初始化块的优先级最高,也就是最先执行,并且仅在类第一次被加载时执行;
  2. 非静态初始化块和构造函数后执行,并且在每次生成对象时执行一次;
  3. 非静态初始化块的代码会在类构造函数之前执行。因此若要使用,应当养成把初始化块写在构造函数之前的习惯,便于调试;
  4. 静态初始化块既可以用于初始化静态成员变量,也可以执行初始化代码
  5. 非静态初始化块可以针对多个重载构造函数进行代码复用

参考

www.cnblogs.com/BlackStorm/…