装载:查找和导入类或接口的二进制数据,常用的是根据类的路径加载,还有根据网络的地址加载。
链接:执行校验、准备和解析步骤,其中解析步骤是可以选择的;
校验:检查导入类或接口的二进制数据的正确性;
准备:给类的静态变量分配并初始化存储空间;
解析:将符号引用转成直接引用;
初始化:激活类的静态变量的初始化Java代码和静态Java代码块。
其中 初始化(initialization)包含两部分:
1.类的初始化(initialization class & interface)---初始化静态变量和静态Java代码块
2.对象的创建(creation of new class instances)----new class()。
用一个简单的流程分析如下:
普通对象初始化 继承体系对象初始化
初始化
1.类的初始化,也就是静态变量以及静态块的初始化
1.1 如果有继承父类有静态变量或者静态块的,先执行初始化。
1.2 如果是final static ,那么就不会初始化类
1.3 如果执行的是父类的静态变量,那么子类不会初始化
public class SuperTmp {
public static int a = 10;
public final static int b = 20;
public int c = 10;
static{
System.out.println("init super calss...");
}
public SuperTmp (){
System.out.println("super init constructor:");
}
}
public class Sub extends SuperTmp {
public static final int aa = 30;
public static int bb = 40;
public int cc = 20;
static {
System.out.println("init sub class.....");
}
public Sub (){
System.out.println("sub init constructor:" );
}
}
public class Play {
public static void main(String[] args) {
/**输出
* init super calss...
* 10
* 不会初始化子类 Sub
*/
System.out.println(Sub.a);
/******
* 输出 b 不会初始化任何类
*/
System.out.println(Sub.b);
/***
* 输出
* init super calss...
* init sub class.....
* 40
*/
System.out.println(Sub.bb);
/*对象初始化
*init super calss...
init sub class.....
*super init constructor
sub init constructor
*/
Sub sb = new Sub()
}
}
2.创建class的对象
2.1.先为Sub类和其父类SuperTmp类分配内存空间,父类和子类的变量都初始化值,对象类型的为null,
基本数据类型的为默认的值,例如 int 类型的为初始化 0
2.2 执行构造函数Sub(), 调用父类的构造函数,输出“super init constructor”,“sub init constructor”。
2.3 初始化superTmp,Sub类的成员变量,并赋值
2.4 对于类实现了接口Interface,初始化它的时候并不会引起接口的初始化。
所以对象的创建,具体步骤如下:
(1) 所有的成员变量—包括该类,及它的父类中的成员变量--被分配内存空间,
并赋予默认值。(第一次初始化成员变量)
(2) 为所调用的构造函数初始化其参数变量。(如果有参数)
(3) 如果在构造函数中用this 调用了同类中的其他构造函数,
则按照步骤(2)~(6)去处理被调用到的构造函数。
(4) 如果在构造函数中用super调用了其父类的构造函数,
则按照步骤(2)~(6)去处理被调用到的父类构造函数。
(5) 按照书写顺序,执行instance initializer 和 instance variable initializer
来初始化成员变量。(第二次初始化成员变量)
(6) 按照书写顺序,执行构造函数的其余部分。
总结:
从类的初始化和对象的创建步骤,可以知道,一个类是先初始化static的变量和static句块,
然后在分配该类以及父类的成员变量的内存空间,赋予默认值,然后开始调用构造函数。
而子类和父类之间,则先初始化和创建父类,然后再初始化和创建子类的。