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