JAVA类初始化顺序及final常量初始化顺序

整理下JAVA类初始化顺序和一点点细节~

类加载顺序

结论

类初始化顺序

验证

父类:

public class Father {

    int fatherNbr = 1;
    static int fatherStaticNbr = 2;

    static {
        System.err.println("---static father block start---");
        System.err.println("fatherStaticNbr=" + fatherStaticNbr);
        System.err.println("---static father block end---");
        System.err.println();
    }

    {
        System.err.println("---father block start---");
        System.err.println("fatherNbr=" + fatherNbr);
        System.err.println("fatherStaticNbr=" + fatherStaticNbr);
        System.err.println("---father block end---");
        System.err.println();
    }

    public Father() {
        System.err.println("---father init start---");
        System.err.println("fatherNbr=" + fatherNbr);
        System.err.println("fatherStaticNbr=" + fatherStaticNbr);
        System.err.println("---father init end---");
        System.err.println();
    }
}

子类:

public class Son extends Father {

    int sonNbr = 100;
    static int sonStaticNbr = 200;

    static {
        System.err.println("---static son block start---");
        System.err.println("sonStaticNbr=" + sonStaticNbr);
        System.err.println("---static son block end---");
        System.err.println();
    }

    {
        System.err.println("---son block start---");
        System.err.println("sonNbr=" + sonNbr);
        System.err.println("sonStaticNbr=" + sonStaticNbr);
        System.err.println("---son block end---");
        System.err.println();
    }
    
    public Son() {
        System.err.println("---son init start---");
        System.err.println("sonNbr=" + sonNbr);
        System.err.println("sonStaticNbr=" + sonStaticNbr);
        System.err.println("---son init end---");
        System.err.println();
    }
}   System.err.println();
    }
}

main:

public class Test {
    public static void main(String[] args) {
        new Son();
    }
}

执行结果:

---static father block start---
fatherStaticNbr=2
---static father block end---

---static son block start---
sonStaticNbr=200
---static son block end---

---father block start---
fatherNbr=1
fatherStaticNbr=2
---father block end---

---father init start---
fatherNbr=1
fatherStaticNbr=2
---father init end---

---son block start---
sonNbr=100
sonStaticNbr=200
---son block end---

---son init start---
sonNbr=100
sonStaticNbr=200
---son init end---


Process finished with exit code 0

由此可见,执行顺序为:
父类static部分 -> 子类static部分
-> 父类非静态变量及非静态代码块 -> 父类构造器
-> 子类非静态变量及非静态代码块 -> 子类构造器

需要注意

  1. 静态代码块不能调用非静态变量
  2. 静态变量和静态代码块之间是按定义的先后顺序执行的,所以不能调用在本静态代码块之后才定义的静态变量

即以下两种场景都会报错:

    int fatherNbr = 1;    

    static {
        // 非静态变量在静态块之后初始化,无法从静态上下文中引用非静态变量
        System.err.println("fatherNbr=" + fatherNbr); 
        // 调用的静态变量在后面才定义,非法前向引用
        System.err.println("fatherStaticNbrAfter=" + fatherStaticNbrAfter);
    }

    static int fatherStaticNbrAfter = 3;

static在构造器之后执行的场景

若先声明静态对象,仍会调用构造器,再执行其他静态代码块:

public class SpecialTest {

	// 先调用了构造方法
    static SpecialTest test = new SpecialTest();

    static {
        System.err.println("static block");
    }

    {
        System.err.println("block");
    }

    int num = 100;
    static int staticNbr = 200; // 第一次执行时,未赋值
    static final int staticFinalNbr = 300;

    SpecialTest() {
        System.err.println("-----init start----");
        System.err.println("num=" + num + " staticNbr=" + staticNbr + " staticFinalNbr=" + staticFinalNbr);
        System.err.println("-----init end----");
    }

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

执行结果:

block
-----init start----
num=100 staticNbr=0 staticFinalNbr=300
-----init end----
static block
block
-----init start----
num=100 staticNbr=200 staticFinalNbr=300
-----init end----

可以观察到,首先执行第一行static SpecialTest test = new SpecialTest();时,调用了构造方法,所以接下来执行的是构造方法,而不是其他静态赋值和静态代码块。

在第一次执行构造方法(static调用)前,会执行非静态代码的赋值和非静态代码块,和final修饰的变量。然后再第一次执行构造方法,由于静态变量赋值未执行,所以打印int的默认值staticNbr=0。之后才继续执行后续的静态代码块和静态变量赋值。
在第二次执行构造方法时(main方法内调用),已执行的静态代码块不会重复执行,非静态代码块会再次执行,然后现在的构造方法才正常打印所有的变量值。

final初始化顺序

在上面的例子可以看到静态常量static final int staticFinalNbr = 300;在第一次执行构造方法时也已经赋值完成。这是因为final对象已经加入常量池。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值