类加载分为三步:
1.加载: 将class文件字节码加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后生成一个代表这个类的java.lang.Class 对象。
2.链接:将java类的二进制代码合并到jvm运行状态的过程
(1)验证: 确保加载的类信息符合JVM规范
(2)准备: 为static修饰的类变量分配没存并设置初始值
(3)解析: 虚拟机常量池的符号引用(常量名)替换为直接引用(地址)的过程
3.初始化:执行类构造器<clinit>() 方法的过程,类构造器<clinit>() 编译期间自动收集所有类变量赋值动作和静态代码块合并产生的。
当初始化一个类时,如果发现其父类还没有进行初始化,先初始化其父类。
什么时候发生类的初始化?
1. 类的主动引用:
(1)main方法 (2)new 一个对象 (3)调用静态成员(除了final常量)和静态方法。
(4)反射调用 )(5)初始化一个类时,如果父类没有初始化,先初始化其父类。
类的被动引用:不会发生初始化
(1)当访问一个静态域时,只有真正声明这个域的类才会初始化。如 当通过子类引用父类的静态变量,不会导致子类初始化。
(2)通过数组定义类引用,不会触发此类的初始化。
(3)引用常量不会触发 因为常量在链接阶段已经放在常量池中
在创建java类的对象时,类中各成员的执行顺序:
1.父类静态成员和静态初始化快,按在代码中出现的顺序依次执行。
2.子类静态成员和静态初始化块,按在代码中出现的顺序依次执行。
3. 父类的实例成员和实例初始化块,按在代码中出现的顺序依次执行。
4.执行父类的构造方法。
5.子类实例成员和实例初始化块,按在代码中出现的顺序依次执行。
6.执行子类的构造方法。
7.静态初始化块只会执行一遍
示例:
public static void main(String[] args) {
Son s = new Son();
s = new Son();
}
}
class Parent {
{
System.out.println("parent中的初始化块");
}
static {
System.out.println("parent中static初始化块");
}
public Parent() {
System.out.println("parent构造方法");
}
}
class Son extends Parent {
{
System.out.println("son中的初始化块");
}
static {
System.out.println("son中的static初始化块");
}
public Son() {
System.out.println("son构造方法");
}
执行结果:
parent中static初始化块
son中的static初始化块
parent中的初始化块
parent构造方法
son中的初始化块
son构造方法
parent中的初始化块
parent构造方法
son中的初始化块
son构造方法
非静态初始化块主要是用于对象的初始化操作,在每次创建对象的时都要调用一次,其执行顺序在构造方法之前。