一. 类和对象的加载过程
类的加载过程
版本一:
- JVM启动时,加载所需要的class文件 。
- JVM加载class文件时,会先加载静态内容(静态成员变量、静态方法、静态代码块)到方法区的静态区中。
- 静态加载完后,会给所有的成员变量默认初始化,以及给静态成员变量开辟空间
- 当给所有的静态成员变量默认初始化完成后,开始按照代码的顺序执行
- 静态都执行完毕(静态属于类),类才彻底加载完成
版本二:
- 一个类要创建实例需要先加载并初始化该类,main方法所在的类需要先加载和初始化
- 一个子类初始化先初始化其父类
- 一个类初始化就是执行clinit方法
- clinit方法由静态类变量显示赋值代码和静态代码块组成
- 类变量显示赋值代码和静态代码块是按照顺序执行
- clinit只会执行一次
对象的加载过程
版本一:
- 当类加载完成后,通过new关键字创建对象,在堆中给对象分配内存空间
- 给对象所属的类的非静态成员变量分配空间并默认初始化
- 调用构造函数先隐式三步
- 执行super()调用父类的无参构造,初始化父类
- 给非静态变量进行显示赋值
- 执行构造代码块
- 再执行构造函数中其它的代码
- 构造函数执行完毕,对象创建完成
版本二:对象的实例化
- 实例初始化就是执行init方法
- init可能重载有多个,有几个构造器就有几个init方法
- init方法由非静态实例变量显示赋值代码和非静态代码块、对应构造器代码块组成。
- 非静态实例变量显示赋值代码和非静态代码块是按照从上到下顺序执行,而对应的构造器代码块最后执行
- 每次创建实例对象,调用对应构造器,执行的就是对应的init方法
- init方法的首行是super()方或者super(实例参数),即父类的的init方法
/**
* 父类的初始化<clinit>();
* (1)j = method()
* (2)父类的静态代码块
*
* 父类的实例化方法
* (1)super()在(最前)
* (2)i = test()
* (3)父类的非静态代码块
* (4)父类的无参构造(最后)
*
* 非静态方法前面其实有一个默认的this
* this在构造器或者<init>(),它表示正在创建的对象,
* 因为正在创建的是Son对象,
* 所以test()执行的是子类重写 的代码块(面对对象的多态)
*
*/
public class Father {
private int i = test();
private static int j = method();
static {
System.out.print("(1)");
}
Father(){
System.out.print("(2)");
}
{
System.out.print("(3)");
}
private int test() {
System.out.print("(4)");
return 1;
}
private static int method() {
System.out.print("(5)");
return 1;
}
}
/**
* 子类的初始化<clinit>();
* (1)j = method()
* (2)子类的静态代码块
*
* 子类的实例化方法
* (1)super()在(最前)
* (2)i = test()
* (3)子类的非静态代码块
* (4)子类的无参构造(最后)
*
*/
public class Son extends Father {
private int i = test();
private static int j = method();
static {
System.out.print("(6)");
}
Son(){
System.out.print("(7)");
}
{
System.out.print("(8)");
}
private int test() {
System.out.print("(9)");
return 1;
}
private static int method() {
System.out.print("(10)");
return 1;
}
public static void main(String[] args) {
Son son1 = new Son();
System.out.println();
Son son2 = new Son();
}
}
打印结果:
(5)(1)(10)(6)(4)(3)(2)(9)(8)(7)
(4)(3)(2)(9)(8)(7)