双亲委派模型
类加载器
- 启动类加载器(Bootstrap ClassLoader):虚拟机将能识别的类库加载到虚拟机内存中
- 拓展类加载器(Extension ClassLoader):负责加载指定的路径中的所有类库
- 应用程序类加载器(Application ClassLoader):负责加载用户类路径上所指定的类库
类加载器的关系
双亲委派模型的操作过程
一个类加载器收到了类加载的请求,它不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父类加载器反馈自己无法完全这个加载请求时,子类加载器才会尝试自己去加载
类加载机制
类从被加载到虚拟机内存中开始,到卸载出内存为止,它的生命周期包括了:加载、验证、准备、解析、初始化、使用、卸载七个阶段,其中验证、准备、解析三个阶段统称为链接阶段
必须对类初始化的情况:
- new一个类的时候;调用或者设置一个类的静态字段(static final 除外)
- 对类进行反射调用的时候,没有经过初始化则先初始化
- 初始化一个类的时候发现父类没有初始化,父类进行初始化
- 执行的main() 方法的时候,虚拟机会先初始化这个类
不会对类初始化的情况:
- 通过子类引用父类中的静态字段,不会初始化子类
public class SuperClass {
public static int value = 123;
static {
System.out.println("SuperClass Init");
}
}
public class SubClass extends SuperClass {
static {
System.out.println("SubClass Init");
}
}
public class NotInitialization {
public static void main(String[] args) {
//通过子类引用父类的静态字段,不会导致子类初始化
System.out.println(SubClass.value);
}
}
- 通过定义数组引用类,不会触发此类的初始化
public class SuperClass {
public static int value = 123;
static {
System.out.println("SuperClass Init");
}
}
public class NotInitialization {
public static void main(String[] args) {
//通过数组定义来引用类,不会触发此类的初始化
SuperClass[] sca = new SuperClass[10];
}
}
- 引用常量池中的常量,不会触发定义常量类的初始化
public class ConstClass {
public static final String HELLOWORLD = "hello world";
static {
System.out.println("ConstClass Inited");
}
}
public class NotInitialization {
public static void main(String[] args) {
// 常量在编译阶段会存入调用类的常量池中
// 本质上没有直接引用到定义常量的类,不会导致该类初始化
System.out.println(ConstClass.HELLOWORLD);
}
}
类加载过程
首先加载过程是在堆上生成一个代表这个类的.Class对象,然后检验class文件的字节流是否符合规范,然后为类变量在方法区分配内存空间,解析接口、类、方法等最后初始化执行父类和当前类的构造方法和静态语句块,为对象和实例变量分配堆空间
- 加载:在java堆中生成一个代表这个类的.Class对象
- 验证:验证class字节流文件中的文件格式、元数据、字节码和符号引用等信息符合虚拟机的要求
- 准备:为类变量在方法区中分配内存,实例变量会随着对象实例化的时候随着对象一起分配到堆中
- 解析:类、接口、类方法、类字段等的解
- 初始化:执行父类和当前类的构造方法和静态语句块,分配对象和实例变量在堆上的空间