- 类加载顺序
- 结论
- 验证
- 需要注意
- static在构造器之后执行的场景
- 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部分
-> 父类非静态变量及非静态代码块 -> 父类构造器
-> 子类非静态变量及非静态代码块 -> 子类构造器
- 静态代码块不能调用非静态变量
- 静态变量和静态代码块之间是按定义的先后顺序执行的,所以不能调用在本静态代码块之后才定义的静态变量
即以下两种场景都会报错:
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方法内调用),已执行的静态代码块不会重复执行,非静态代码块会再次执行,然后现在的构造方法才正常打印所有的变量值。
在上面的例子可以看到静态常量static final int staticFinalNbr = 300;在第一次执行构造方法时也已经赋值完成。这是因为final对象已经加入常量池。



