静态变量、静态块、构造块、构造函数执行顺序
先看一个demo:
Parent类:
/**
* Created by flh on 2017/8/20.
*/
public class Parent {
Parent(){
System.out.println("我是父类构造函数");
}
static {
System.out.println("我是父类静态代码块2");
}
public static int i = getInt();
public static int getInt(){
System.out.println("父类中静态变量初始化");
return 0;
}
static {
System.out.println("我是父类静态代码块1");
}
{
System.out.println("我是父类构造块2");
}
{
System.out.println("我是父类构造块1");
}
}
Child类:
public class Child extends Parent{
public static int i = getInt();
Child(){
System.out.println("我是子类构造函数");
}
public static int getInt(){
System.out.println("子类中静态变量初始化");
return 0;
}
static {
System.out.println("我是子类静态代码块");
}
{
System.out.println("我是子类构造块");
}
public static void main(String[] args) {
Parent p = new Child();
System.out.println("若已经加载过--------------------------");
Parent p2 = new Child();
}
}
输出结果顺序:
我是父类静态代码块2
父类中静态变量初始化
我是父类静态代码块1
子类中静态变量初始化
我是子类静态代码块
我是父类构造块2
我是父类构造块1
我是父类构造函数
我是子类构造块
我是子类构造函数
若已经加载过--------------------------
我是父类构造块2
我是父类构造块1
我是父类构造函数
我是子类构造块
我是子类构造函数
可以得出:
如果未加载过,父类静态代码块/静态变量到子类静态代码块/静态变量 --父类构造块 --父类构造函数 --子类构造块--子类构造函数
注意两点(原理分析):
(1)静态变量和静态代码块只会加载一次,先父类再子类,按照具体顺序来。如果已加载过,则跳过这步。
原理:类的初始化是类加载过程的最后一步,也是执行类构造器<clinit>()方法的过程。<clinit>()方法是由编译器自动收集类中的所有静态变量和静态语句块中的语句合并产生的。<clinit>()方法与类的构造函数不同,它不需要显示调用父类构造器,虚拟机会保证在子类的<clinit>()方法执行之前,父类的<clinit>()方法执行完毕,也就意味着父类中定义的静态块静态变量执行优先于子类。
(2)构造块先于构造函数。(先父类中的构造块和构造函数,再子类)
原理:构造代码块中定义的是不同对象共性的初始化内容,给所有对象进行统一初始化;而构造函数是给对应的对象初始化。
结论:
1.父类的静态变量、父类的静态代码块(谁在前,谁先初始化)
2.子类的静态变量、子类的静态代码块(谁在前,谁先初始化)
3.父类的非静态变量、父类的非静态代码块(谁在前,谁先初始化)、父类的构造函数
4.子类的非静态变量、子类的非静态代码块(谁在前,谁先初始化)、子类的构造函数