## 类加载
> 参考: [Java类加载过程](https://www.cnblogs.com/xiaoxian1369/p/5498817.html)
类加载分为以下七个阶段
**加载➡验证➡准备➡解析➡初始化➡使用➡卸载**
上述阶段可以简化为
**加载➡链接➡初始化➡使用➡卸载**
1. 加载
简单的说,类加载阶段就是由类加载器负责根据一个类的全限定名来读取此类的二进制字节流到JVM内部,并存储在运行时内存区的方法区,然后将其转换为一个与目标类型对应的java.lang.Class对象实例,作为后面各种数据的访问入口
2. 验证
验证类数据信息是否符合JVM规范,是否是一个有效的字节码文件,验证内容涵盖了类数据信息的格式验证、语义分析、操作验证等。
3. 准备
为类中的所有静态变量分配内存空间,并为其设置一个初始值
被final修饰的静态变量,会直接赋予原值;类字段的字段属性表中存在ConstantValue属性,则在准备阶段,其值就是ConstantValue的值
4. 解析
将常量池中的符号引用转为直接引用(得到类或者字段、方法在内存中的指针或者偏移量,以便直接调用该方法),这个可以在初始化之后再执行。
**什么是符号引用和直接引用**
在编译时,**java类并不知道引用类的实际内存地址,因此只能使用符号引用来代替。** 比如org.simple.People类引用org.simple.Tool类,在编译时People类并不知道Tool类的实际内存地址,因此只能使用符号org.simple.Tool(假设)来表示Tool类的地址。**而在类装载器装载People类时,此时可以通过虚拟机获取Tool类的实际内存地址,因此便可以既将符号org.simple.Tool替换为Tool类的实际内存地址,及直接引用地址。**
可以认为是一些静态绑定的会被解析,动态绑定则只会在运行是进行解析;静态绑定包括一些final方法(不可以重写),static方法(只会属于当前类),构造器(不会被重写)
5. 初始化
将一个类中所有被static关键字标识的代码统一执行一遍,如果执行的是静态变量,那么就会使用用户指定的值覆盖之前在准备阶段设置的初始值;如果执行的是static代码块,那么在初始化阶段,JVM就会执行static代码块中定义的所有操作。
在这里可以看出,静态代码块肯定是先于其他代码块执行的。
6. 使用
7. 卸载
## ClassLoader
> 参考自: [Java自定义类加载器与双亲委派模型](https://www.cnblogs.com/wxd0108/p/6681618.html)
## Java类装载方式:
1. 隐式装载: 运行过程中碰到new等方法生成对象时,如果类没有被加载到JVM中,那么隐式调用类加载器装载
2. 显式装载:通过Class.forName()(静态)等方法 显式加载需要的类
## Java类装载器
1. Bootstrap Loader(启动类加载器)
是用C++语言写的、在JVM启动后初始化。主要负责%JAVA_HOME%/jre/lib、-Xbootclasspath、%JAVA_HOME%/jre/classes等路径的类库加载到内存中。由于它是由C++编写的,所以在java里面看不见他。
2. Extension ClassLoader(扩展类加载器)
Bootstrap loder加载ExtClassLoder。ExtClassLoder负责加载<JAVA_HOME>\lib\ext目录或java.ext.dirs系统变量指定的路径中的所有类库,继承自抽象类ClassLoder
3. Application ClassLoader(应用程序类加载器)
负责加载用户类路径(classpath)上的指定类库,我们可以直接使用这个类加载器。一般情况,如果我们没有自定义类加载器默认就是用这个加载器。
## 双亲委派模型
双亲委派模型工作过程是:**如果一个类加载器收到类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器完成。** 每个类加载器都是如此,只有当父加载器在自己的搜索范围内找不到指定的类时(即ClassNotFoundException),子加载器才会尝试自己去加载。
### 为什么需要双亲委派模型?
假设一个场景。
黑客自定义一个java.lang.String类,该String类具有系统的String类一样的功能,只是在某个函数稍作修改。比如equals函数,这个函数经常使用,如果在这这个函数中,黑客加入一些“病毒代码”。并且通过自定义类加载器加入到JVM中。此时,如果没有双亲委派模型,那么JVM就可能误以为黑客自定义的java.lang.String类是系统的String类,导致“病毒代码”被执行。
而有了双亲委派模型,黑客自定义的java.lang.String类永远都不会被加载进内存。因为首先是最顶端的类加载器加载系统的java.lang.String类,最终自定义的类加载器无法加载java.lang.String类。
或许你会想,我在自定义的类加载器里面强制加载自定义的java.lang.String类,不去通过调用父加载器不就好了吗?确实,这样是可行。但是,**在JVM中,判断一个对象是否是某个类型时,如果该对象的实际类型与待比较的类型的类加载器不同,那么会返回false。**
<br />
<br />
<br />
<br />
<div align="right">
Chen Sicong
搬运时间:2019年8月2日 22:32:51
</div>
【学习】类加载、ClassLoader和双亲委派