类加载
平常写的代码是保存在一个 .java
文件里面,经过编译会生成.class
文件,这个文件存储的就是字节码,如果要用上我们的代码,那就必须把它加载到 JVM 中。
当然,加载到 JVM 生成 class 对象的来源不一定得是.class
文件,也可以来自网络等等,反正只要是符合 JVM 规范的都行。
而类加载的步骤主要分为:加载、链接、初始化。
加载
其实就是找到字节流,然后将其加载到 JVM 中,生成类对象。
链接
这个阶段是要让生成的类对象融入到 JVM 中。
验证就是检验一下加载的类是否满足 JVM 的约束条件,也就是判断是否合规。
准备就是为加载类的静态变量申请内存空间,并赋予初始值,例如是 int 类型那初始值就是 0。
解析就是将符号引用解析成为实际引用,讲人话就是:例如 Yes 类里面引用了一个 XX 类,那一开始 Yes 类肯定不知道 XX 类在内存里面的地址,所以就先搞个符号引用替代一下,假装知道,等类加载解析的时候再找到 XX 类真正地址,做一个实际引用。
这就是解析要做的事情。还有一点,虽说把解析放到链接阶段里面,但是 JVM 规范并没有要求在链接过程中完成解析。
初始化
这个阶段就是为常量字段赋值,然后执行静态代码块,将一堆要执行的静态代码块方法包装成 clinit
方法执行,这个方法会加锁,由 JVM 来保证 clinit
方法只会被执行一次。
所以可以用一个内部静态类来实现延迟初始化的单例设计模式,同时保证了线程安全。
===================================》》》》
双亲委派模型
就是子类加载器先让父类加载器去查找该类来加载,父类又继续请求它的父类直到最顶层,在父类加载器没有找到所请求的类的情况下,子类加载器才会尝试去加载。
三种类加载器:
-
启动类加载器(Bootstrap ClassLoader),它是属于虚拟机自身的一部分,用 C++ 实现的,主要负责加载
<JAVA_HOME>lib
目录中或被 -Xbootclasspath 指定的路径中的并且文件名是被虚拟机识别的文件。它是所有类加载器的爸爸。 -
扩展类加载器(Extension ClassLoader),它是 Java 实现的,独立于虚拟机,主要负责加载
<JAVA_HOME>libext
目录中或被 java.ext.dirs 系统变量所指定的路径的类库。 -
应用程序类加载器(Application ClassLoader),它是Java实现的,独立于虚拟机。主要负责加载用户类路径(classPath)上的类库,如果我们没有实现自定义的类加载器那这玩意就是我们程序中的默认加载器。
注释:如果你也定义了一个 java.lang.Object
类,通过双亲委派模式是会把这个请求委托给启动类加载器,它扫描<JAVA_HOME>lib
目录就找到了 jdk 定义的 java.lang.Object
类来加载,所以压根不会加载你写的 java.lang.Object
类,这就可以避免一些程序不小心或者有意的覆盖基础类。