梳理下Java中对象创建时的加载顺序
public class B extends A {
static C c = new C();
static {
System.out.println("子类静态代码块");
}
{
System.out.println("子类构造代码块");
}
public B(){
System.out.println("子类构造函数");
}
public static void main(String[] args) {
B b = new B();
}
}
class A {
static {
System.out.println("父类静态代码块");
}
{
System.out.println("父类构造代码块");
}
public A(){
System.out.println("父类构造函数");
}
}
class C {
static {
System.out.println("静态代码块");
}
{
System.out.println("构造代码块");
}
public C(){
System.out.println("构造函数");
}
}
控制台输出
父类静态代码块
静态代码块
构造代码块
构造函数
子类静态代码块
父类构造代码块
父类构造函数
子类构造代码块
子类构造函数
对象创建时,代码运行顺序(优先级): 父类静态代码块 -> 子类静态代码块 -> 父类构造代码块 -> 父类构造函数 -> 子类构造代码块 -> 子类构造函数
- 父类先于子类进行初始化;
- classloader懒加载, 类被调用时才会加载类
- 静态变量与静态代码块随着类被加载而调用
- 实例成员变量和代码块随着对象初始化(new或反射创建对象)而调用
- 静态变量的优先级与静态代码块等同, 实例成员变量的优先级与构造代码块等同。
- 如果类中同时有静态变量和静态代码块,执行顺序按照代码编写顺序先后。
为什么构造代码块先于构造函数运行?
javac 将XXX.java编译成XXX.class时会将构造代码块插入到构造函数之前。
反编译 A.class
...
public A(){
System.out.println("父类构造代码块");
System.out.println("父类构造函数");
}
...
public class B
{
public static B t1 = new B();
public static B t2 = new B();
{
System.out.println("构造块");
}
static
{
System.out.println("静态块");
}
public static void main(String[] args)
{
B t = new B();
}
}
输出顺序为: 构造块 静态块 构造块 构造块