目录
四、类加载前和加载后的执行顺序(转自http://blog.csdn.net/mrzhoug/article/details/51581994)
一、类加载做了哪些事?
1.之前没有进行类加载
1.类加载,同时初始化类中静态的属性(赋默认值)
2.执行静态代码块
3.分配内存空间,同时初始化非静态的属性(赋默认值)
4.如果声明属性的同时有显示的赋值,那么进行显示赋值把默认值覆盖
5.执行匿名代码块
6.执行构造器
7.返回内存地址
二、类加载的顺序
1.static 变量
2.static 代码块
3.成员变量
4.匿名块
5.构造器
ps:先加载父类,再加载子类;
三、一个具体说明的例子
其中D继承C,C继承B,E,F是单独的类,主方法在ExaminationDemo 中。
public class ExaminationDemo {
public static void main(String[] args) {
System.out.println("1运行 ExaminationDemo 中的 main 函数, 创建 D 类实例");
new D();
}
}
class E {
// E的构造器
E() {
System.out.println("8执行 E 的构造函数");
}
// E的普通成员方法
public void funcOfE() {
System.out.println("12执行 E 的函数");
}
}
class F {
// F的构造器
F() {
System.out.println("2执行 F 的构造函数");
}
// F的普通成员方法
public void funcOfF() {
System.out.println("4执行 F 的函数");
}
}
class B {
//new了一个E对象
E e = new E();
//B的静态成员变量
static F f = new F();
//B的普通成员变量
public String sb = getSb();
//B的静态代码块
static {
System.out.println("3执行 B 类的 static 块(B 包含 E 类的成员 变量,包含静态 F 类成员变量)");
f.funcOfF();
}
//B的匿名代码块
{
System.out.println("10执行 B 实例的普通初始化块");
}
//B的构造器
B() {
System.out.println("11执行 B 类的构造函数(B 包含 E 类的成员变 量,包含静态 F 类成员变量)");
}
//B的普通成员方法
public String getSb() {
System.out.println("9初始化 B 的实例成员变量 sb");
return "sb";
}
}
class C extends B {
// C的静态代码块
static {
System.out.println("5执行 C 的 static 块(C 继承 B)");
}
// C的匿名代码块
{
System.out.println("13执行 C 的普通初始化块");
}
// C的构造器
C() {
System.out.println("14执行 C 的构造函数(C 继承 B)");
}
}
class D extends C {
// D的静态成员变量
public String sd1 = getSd1();
// D的普通成员变量
public static String sd = getSd();
// D的静态代码块
static {
System.out.println("7执行 D 的 static 块(D 继承 C)");
}
// D的匿名代码块
{
System.out.println("16执行 D 实例的普通初始化块");
}
// D的构造器
D() {
System.out.println("17执行 D 的构造函数(D 继承 C);父类 B 的实 例成员变量 sb 的值为:" + sb + ";本类 D 的 static 成员变量 sd 的值为:" + sd
+ "; 本类 D 的实例成员变量 sd1 的值是:" + sd1);
}
// D的静态成员方法(调用时才加载)
static public String getSd() {
System.out.println("6初始化 D 的 static 成员变量 sd");
return "sd";
}
// D的普通成员方法
public String getSd1() {
System.out.println("15初始化 D 的实例成员变量 sd1");
return "sd1";
}
}
从上面的代码我们可以看出,加载主方法时,先执行了输出语句,然后是new D(),由此进行加载顺序的分析。
执行结果如下:
/*
* 1运行 ExaminationDemo 中的 main 函数, 创建 D 类实例
* 2执行 F 的构造函数
* 3执行 B 类的 static 块(B 包含 E类的成员 变量,包含静态 F 类成员变量)
* 4执行 F 的函数
* 5执行 C 的 static 块(C 继承 B)
* 6初始化 D 的 static 成员变量sd
* 7执行 D 的 static 块(D 继承 C)
* 8执行 E 的构造函数
* 9初始化 B 的实例成员变量 sb
* 10执行 B 实例的普通初始化块
* 11执行 B 类的构造函数(B 包含 E 类的成员变 量,包含静态 F 类成员变量)
* 13执行 C 的普通初始化块
* 14执行 C 的构造函数(C 继承B)
* 15初始化 D 的实例成员变量 sd1
* 16执行 D 实例的普通初始化块
* 17执行 D 的构造函数(D 继承 C);父类 B 的实 例成员变量 sb的值为:sb;本类 D 的 static 成员变量 sd 的值为:sd; 本类 D 的实例成员变量 sd1 的值是:sd1
*/
这个结果是怎么来的,看下图:
总体上还是按照二的加载顺序来的,需要注意的是,当遇到子父类时,加载顺序是:
父类的静态成员变量,静态代码块——>子类的静态成员变量,静态代码块——>父类的普通成员变量,匿名代码块,构造器
——>子类的普通成员变量,匿名代码块,构造器
并且如果有new 类对象,那么先于普通成员变量加载
而,当成员变量是方法时,就要调用该方法。
无论是静态成员方法,还是普通成员方法,只有在调用时才被加载
四、类加载前和加载后的执行顺序(转自http://blog.csdn.net/mrzhoug/article/details/51581994)
如果类还没有被加载:
1、先执行父类的静态代码块和静态变量初始化,并且静态代码块和静态变量的执行顺序只跟代码中出现的顺序有关。
2、执行子类的静态代码块和静态变量初始化。
3、执行父类的实例变量初始化
4、执行父类的构造函数
5、执行子类的实例变量初始化
6、执行子类的构造函数
如果类已经被加载:
则静态代码块和静态变量就不用重复执行,再创建类对象时,只执行与实例相关的变量初始化和构造方法。