一、什么是类加载
JVM虚拟机把描述类的数据加载到内存里面,并对数据进行验证、准备、解析及初始化,最终变成可以被虚拟机直接使用的class对象。
在 JVM 中,ClassLoader 负责将 *.class 字节码文件加载至内存,经过验证、准备、解析及初始化等步骤,最终形成可供虚拟机直接使用的 java.lang.Class 对象,此过程即类加载。
二、类加载的过程
过程:加载、验证、准备、解析和初始化 。谐音简化记忆:"家宴准备了西式菜" 。
**(1)加载(Loading):**通过类的完全限定性类名读取字节码文件
**(2)验证(Verification):**检查加载的字节码文件的格式
**(3)准备(Preparation):**给类变量分配内存并设置初始值(通常为数据类型的零值,而不是用户定义的初始值)
(4)解析(Resolution):将常量池的符号引用替换成直接引用
(5)初始化: 执行类的构造器方法***<clinit>()** 线程安全的*,包括静态变量的赋值动作和静态语句块的执行。在初始化阶段,静态变量才会被设置为用户定义的初始值。(初始化过程是调用类构造器的过程)
三、类加载的时机
1. 主动引用
(1)New一个对象,即创建一个类的实例对象
(2)程序调用类的静态方法
(3)程序给静态变量赋值
(4)程序访问类的静态变量
(5)反射调用:Class.forName("....") 或是 newInstance()
注意:
(1)加载一个类,如果其父类还未加载,则先触发该父类的加载;
(2)虚拟机会先加载执行main() 方法的类;
(3)当一个接口定义了JDK8新加入的默认方法时,如果有这个接口的实现类发生了加载,则该接口要在实现类之前被加载。
2. 被动引用
除主动引用之外,所有引用类的方式都不会触发加载,称为被动引用。
(1)通过子类引用父类的静态字段,不会导致子类加载。
(2)通过数组来引用类,不会触发类加载。该过程会对数组加载。
(3)常量在编译阶段会存入调用类的常量池中,本质上并没有直接引用到定义常量得类,因此不会触发定义常量的类的加载。
java
package com.apesource.demo01;
/**
* 类加载的时机
* (1)主动引用
* (2)别动引用
*
*/
public class Test01 {
public static void main(String[] args) {
/*************************主动引用*************************************/
// try {
// Class.forName("com.apesource.demo01.Demo");
// } catch (ClassNotFoundException e) {
// e.printStackTrace();
// }
/***************************主动引用***********************************/
// new Demo();
// new Son();
/***************************主动引用***********************************/
// Demo.value = 521;
// Demo.dosth();
/***************************被动引用***********************************/
// int[] arr = new int[10];
System.out.println(Demo.class);
System.out.println(Son.value);
}
}
class Demo{
static {
System.out.println("Demo类被加载了");
}
static int value = 123;
public static void dosth() {
}
}
class Son extends Demo{
static {
System.out.println("Son类被加载了");
}
}
四、什么是类加载器?常见的类加载器有哪些?
类加载器是指通过一个类的全限定性类名获取该类的二进制字节流叫做类加载器;
类加载器分为以下四种:
**(1)启动类加载器(BootStrapClassLoader):**用来加载java核心类库,无法被java程序直接引用;
(2)扩展类加载器(Extension ClassLoader): 用来加载java的扩展库,java的虚拟机实现会提供一个扩展库目录,该类加载器在扩展库目录里面查找并加载java类;
(3)应用程序加载器(AppClassLoader): 它根据java的类路径来加载类,,一般来说,java应用的类都是通过它来加载的;
**(4)自定义类加载器:**由java语言实现,继承自ClassLoader;
五、什么是双亲委派模型?为什么需要双亲委派模型?怎么打破双亲委派模型?
定义: 当一个类加载器收到一个类加载的请求,他首先不会尝试自己去加载,而是将这个请求委派给父类加载器去加载,只有父类加载器在自己的搜索范围类查找不到给类时,子加载器才会尝试自己去加载该类;
**原因:**为了防止内存中出现多个相同的字节码;因为如果没有双亲委派的话,用户就可以自己定义一个java.lang.String类,那么就无法保证类的唯一性。(类唯一性、安全性)
**打破双亲委派模型:**自定义类加载器,继承ClassLoader类,重写loadClass方法和findClass方法。
六、Java对象的创建过程
JVM遇到一条新建对象的指令时首先去检查这个指令的参数是否能在常量池中定义到一个类的符号引用。然后加载这个类(类加载过程在后边讲)
为对象分配内存。一种办法"指针碰撞"一种办法"空闲列表",最终常用的办法"本地线程缓冲分配'(TLAB)"
将除对象头外的对象内存空间初始化为0
对对象头进行必要设置