#类的加载过程
-
装载:查找和导入Class文件;
-
链接:把类的二进制数据合并到JRE中;
-
校验:检查载入Class文件数据的正确性;
-
准备:给类的静态变量分配存储空间;
-
解析:将符号引用转成直接引用;
-
-
初始化:对类的静态变量,静态代码块执行初始化操作
#类初始化
-
遇到new、getstatic、putstatic或invokestatic这4条字节码指令时,如果类没有进行过初始化,则需要先触发其初始化。生成这4条指令的最常见的Java代码场景是:使用new关键字实例化对象的时候,读取或设置一个类的静态字段(被final修饰、已在编译期把结果放入常量池的静态字段除外)的时候,以及调用一个类的静态方法的时候。
-
使用java.lang.reflect包的方法对类进行反射调用的时候,如果类没有进行过初始化,则需要先触发其初始化。
-
当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化。
-
当虚拟机启动时,用户需要指定一个要执行的主类(包含main()方法的那个类),虚拟机会先初始化这个主类。
#实例化与初始化
- 实例化是初始化的其中一部分,初始化还包括类本身的加载,比如静态代码的执行和静态成员变量的初始化。
- 实例化就是new一个新的对象到堆内存空间,但静态化的代码就是类本身拥有的内存空间,被所有new的实例对象共享。
- 不管new多少对象,类的静态代码部分只执行一次,就是在初始化时。
#一个demo ###代码
class StaticInitParent {
static int i = 1;
static int j;
static {
int i = 10;
System.out.println("父类静态代码块1, i = " + i + ", j = " + j);
}
public StaticInitParent() {
System.out.println("父类构造函数");
{
System.out.println("父类构造代码块");
}
}
{
System.out.println("父类普通代码块");
}
static void print(){
System.out.println("父类静态方法 i = " + i + ", j = " + ++j);
}
static {
System.out.println("父类静态代码块2, j = " + j);
}
}
public class StaticInit extends StaticInitParent{
static {
i++;
System.out.println("子类静态代码块1, i = " + i + ", j = " + j);
}
public StaticInit() {
System.out.println("子类构造函数");
{
System.out.println("子类构造代码块");
}
}
{
System.out.println("子类普通代码块");
}
static void print(){
System.out.println("子类静态方法 i = " + i + ", j = " + ++j);
}
static {
System.out.println("子类静态代码块2, j = " + j);
}
public static void main(String[] args){
System.out.println("=====");
StaticInit.print();
StaticInit sta = new StaticInit();
}
}
###运行结果
父类静态代码块1, i = 10, j = 0
父类静态代码块2, j = 0
子类静态代码块1, i = 2, j = 0
子类静态代码块2, j = 0
=====
子类静态方法 i = 2, j = 1
父类普通代码块
父类构造函数
父类构造代码块
子类普通代码块
子类构造函数
子类构造代码块
###分析
- 首先加载父类的static变量和方法,顺序 和代码顺序有关;
- 静态代码块中的变量是内部有效的,这样能理解变量 i 的输出;
- 然后加载子类的static变量和方法,顺序同样与代码顺序有关;
- 子类继承了父类静态变量i的值1,在执行i++后输出2;
- 执行main()方法;
- 根据代码顺序,执行子类的静态函数;
- 接下来开始实例化,加载父类的普通代码块;
- 这里也可以看出并没有再次执行静态代码部分;
- 加载父类的构造函数(内部执行顺序与代码顺序有关);
- 加载子类的普通代码块;
- 加载子类的构造函数,实例化结束;
#结论 当该类有父类的时候,类的加载过程是:(以下内容均处于初始化阶段)
父类的static成员变量和方法-->该类的static变量和方法-->main()方法-->父类的普通成员变量和方法-->父类的构造方法->该类的普通成员变量和方法-->该类的构造方法-->结束