1.类加载
描述:当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过类的加载,类的连接,类的初始化这三个步骤来对类进行初始化。
类的加载:将class文件读入内存,并为之创建一个 java.lang.Class 对象。任何类被使用时,系统都会为之建立一个 java.lang.Class 对象
类的连接:
1.验证阶段:用于检验被加载的类是否有正确的内部结构,并和其他类协调一致
2准备阶段.负责为类的类变量分配内存,并设置默认值
(不是初始化值,例如static int i = 8,在这个步骤是先将 i 赋值为0。)
3.解析阶段:将类的二进制数据中的符号引用替换为直接引用
类的初始化:
在该阶段,主要就是对类变量进行初始化
(就上面的例子:此时才将 i 初始化赋值为8)
图示:
类的初始化步骤
- 假如类还未被加载和连接,则程序先加载并连接该类(可以通过new关键字,Class.forName方法等来加载类)
- 假如该类的直接父类还未被初始化,则先初始化其直接父类
- 假如类中有初始化语句,则系统依次执行这些初始化语句
- 注意:在执行第2个步骤的时候,系统对直接父类的初始化步骤也遵循初始化步骤1-3
类的加载时机
总结一句话:凡是要用到这个类,就需要去加载。
2.JVM的类加载机制
- 全盘负责:就是当一个类加载器负责加载某个Class时,该Class所依赖的和引用的其他Class也将由该类加载器负责载入,除非显示使用另外一个类加载器来载入
- 父类委托:就是当一个类加载器负责加载某个Class时,先让父类加载器试图加载该Class,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类
- 缓存机制:保证所有加载过的Class都会被缓存,当程序需要使用某个Class对象时,类加载器先从缓存区中搜索该Class,只有当缓存区中不存在该Class对象时,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存储到缓存区
3.类加载器
类加载器的作用:负责将.class文件加载到内存中,并为之生成对应的Class对象,也就是字节码文件对象
内置类加载器
- 引导/启动类加载器(Bootstrap ClassLoader):
用于加载系统类库<JAVA_HOME>\bin目录下的class,例如:Object String 等JDK中的核心类。 - 扩展类加载器(Extension ClassLoader):
用于加载扩展类库<JAVA_HOME>\lib\ext目录下的class。这些类给JDK自己用的。 - 应用程序类加载器(Application ClassLoader):
用于加载我们自定义类的加载器。
类加载器的父子关系:Application 的父加载器为Extension ,而Extension 的父加载器为Bootstrap (只是这么称呼而已,并没有实际上的继承关系)
图示:
自定义加载器(Custom Classloader):通常是我们为了某些特殊目的实现的自定义加载器
4.双亲委派机制
classloader的双亲委托机制是指多个类加载器之间存在父子关系的时候,某个class类具体由哪个加载器进行加载的问题。其具体的过程表现为:当一个类加载的过程中,它首先不会去加载,而是委托给自己的父类去加载,父类又委托给自己的父类。因此所有的类加载都会委托给顶层的父类,即Bootstrap Classloader进行加载,然后父类自己无法完成这个加载请求,子加载器才会尝试自己去加载
双亲委派机制的好处:
1.确保只有一个类加载器可以加载这个类,能够保证类在内存中的唯一性
2.保证安全性。例如:如果你用String类来存储密码的话,可以自定义一个String类加载器,就可以随意控制这些数据。显然这是不安全的。