类加载顺序

时间:2024-03-30 09:53:45

目录

一、类加载做了哪些事?

二、类加载的顺序

三、一个具体说明的例子

四、类加载前和加载后的执行顺序(转自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、执行子类的构造函数 

如果类已经被加载: 
则静态代码块和静态变量就不用重复执行,再创建类对象时,只执行与实例相关的变量初始化和构造方法。