java类的加载顺序
一.java类的加载顺序
总结一下顺序:
- 父类静态变量显式赋值、父类静态代码块(按定义顺序)
- 子类静态变量显式赋值、子类静态代码块(按定义顺序)
- 父类非静态变量显式赋值(父类实例成员变量)、父类非静态代码块(按定义顺序)【注意:子类可能覆盖了父类的普通函数】
- 父类构造函数
- 子类非静态变量(子类实例成员变量)、子类非静态代码块(按定义顺序)
- 子类构造函数。
Q: 什么时候会进行静态变量的赋值和静态代码块的执行?
A:
- 第一次创建某个类或者某个类的子类的实例
- 访问类的静态变量、调用类的静态方法
- 使用反射方法forName
- 调用主类的main方法(本例子的第一次静态初始化其实属于这个情况,调用了Dog的main方法)
注: 类初始化只会进行一次, 上面任何一种情况触发后,之后都不会再引起类初始化操作。
public class Animal {
public int i = test();
public static int j = method();
static {
System.out.println("a: " + "父类静态代码块");
}
Animal(){
System.out.println("b: " + "父类构造函数");
}
{
System.out.println("c: " + "父类普通代码块");
}
public int test(){
System.out.println("d: " + "父类普通变量i");
return 1;
}
public static int method(){
System.out.println("e: " + "父类静态变量j");
return 1;
}
}
public class Dog extends Animal{
{
System.out.println("h: " + "子类普通代码块");
}
public int i = test();
static {
System.out.println("f: " + "子类静态代码块");
}
public static int j = method();
Dog(){
System.out.println("g: " + "子类构造函数");
}
public int test(){
System.out.println("i: " + "子类普通i");
return 2;
}
public static int method(){
System.out.println("j: " + "子类静态变量j");
return 2;
}
public static void main(String[] args) {
Dog dog = new Dog();
System.out.println();
Dog dog1 = new Dog();
System.out.println();
}
}
结果:
e: 父类静态变量j
a: 父类静态代码块
f: 子类静态代码块
j: 子类静态变量j
i: 子类普通i //子类覆盖了父类的普通函数导致的
c: 父类普通代码块
b: 父类构造函数
h: 子类普通代码块
i: 子类普通i
g: 子类构造函数
i: 子类普通i //只有首次会将静态代码块以及静态变量初始化
c: 父类普通代码块
b: 父类构造函数
h: 子类普通代码块
i: 子类普通i
g: 子类构造函数
补充:清空main方法再执行main(调用主类Dog的main方法静态代码块和静态变量执行初始化)
1 public static void main(String[] args) {
2 // Dog dog = new Dog();
3 // System.out.println();
4 // Dog dog1 = new Dog();
5 //
6 // System.out.println();
7 }
输出:
e: 父类静态变量j
a: 父类静态代码块
f: 子类静态代码块
j: 子类静态变量j