1、类的加载过程java
一、加载:c++
1.一、经过类的全限定名来获取类的二进制字节流。编程
1.二、将字节流转换为方法区的运行时数据结构。安全
1.三、在内存中生成表明该类的java.lang.Class对象,做为该类的访问入口。网络
二、验证数据结构
2.一、验证字节流是否符合Class文件规范。多线程
2.二、对字节码描述的语义进行分析。spa
2.三、验证语义的合法性。线程
三、准备指针
3.一、在方法区中,为类变量(static修饰的变量)分配内存,并赋初始化值。(初始化值:根据数据的默认类型设置零值)
初始化值举例:
public static int value = 123;
那么准备阶段执行完后,value = 0 。在初始化阶段,才变为 value = 123 。
四、解析
4.一、将常量池的符号引用替换为直接引用。
符号引用:符号引用以一组符号来描述所引用的目标,符号能够是任何形式的字面量,只要能惟一标识目标便可。
直接引用:直接引用能够是直接指向目标的指针、相对偏移量或是一个能间接定位到目标的句柄。
举例:
public class A{
private List<Object> lists;
}
用类A的加载器,根据List的全限定名,加载List类,并将加载后的List类的访问入口,指向lists这个变量。(符号引用替换为直接引用)
五、初始化
5.一、初始化阶段,编译器将执行全部类变量、静态语句块的赋值动做。
执行初始化的顺序为:
根据语句在源文件中的前后顺序来顺序执行。先执行父类的初始化,再执行子类的初始化。
注意:
虚拟机会保证多线程环境下,初始化过程只能执行一次。
2、类加载器
类加载器:经过类的全限定名来获取类的二进制字节流。
类加载器分为两种:
启动类加载器:c++语言实现,虚拟机的一部分。
其余类加载器:继承于java.lang.ClassLoader。
启动类加载器:负责将<JAVA_HOME>\lib目录下的,类库加载到虚拟机中。
扩展类加载器:加载<JAVA_HOME>\lib\ext 目录中的类,能够被开发者直接使用。
应用程序加载器:负责加载用户类库路径(ClassPath)中的类库,能够被开发者直接使用。
自定义类加载器:加载自定义的类库。
双亲委派模型工做过程:
若是一个类加载器收到了类的加载请求,它首先不会本身去尝试加载这个类,而是把这个加载请求委派给父类加载器去完成,每个层次的类加载器都是如此。所以全部的加载请求最终都会传递到顶层的启动类加载器中,只有当父类加载器反馈本身没法完成加载请求(在本身的搜索范围内,找不到该类)时,子类才会去尝试本身去加载。
双亲委派模型的意义:
一、确保惟一性。肯定一个class的惟一性是由类加载器和对应的class来惟一肯定的。若是不适应双亲模型,那么根类加载器和系统类加载器都加载了一个名为String的类,那么在使用的时候,值同样的对象equals就不相等。
二、安全性。防止经过网络的形式,篡改系统库,即rt.jar。
双亲委托模型的缺点:
一、父加载器所加载的类,没法使用子加载器所加载的类。(解决的方法:线程上下文类加载类)
每个类都会使用自身的类加载器来加载所依赖的类。
线程上下文类加载器:
在面向接口的编程环境下,接口类是类库定义的,实现类是任意的。那么启动类加载器能够加载到接口类,可是如何识别实现类呢?此时就须要到线程上下文类加载器。
线程上下文类加载器(Thread Context ClassLoader)。Thread类中有getContextClassLoader()和setContextClassLoader(ClassLoader cl)方法用来获取和设置上下文类加载器,若是没有setContextClassLoader(ClassLoader cl)方法经过设置类加载器,那么线程将继承父线程的上下文类加载器,若是在应用程序的全局范围内都没有设置的话,那么这个上下文类加载器默认就是应用程序类加载器(Application ClassLoader),换句话说Java默认的线程上下文类加载器就是应用程序类加载器(AppClassLoader)。
通常使用模式:
类的卸载:仅自定义加载器所加载的类,才能进行卸载。