1.类加载/类初始化的概念 2.java提供的类加载器 3.双亲委派机制 4.拓展:类实例化过程 5.知识推荐:自定义类加载器博客一、类加载概念 1、概念
当程序要使用某个类是,如果该类还未被加载到内存中,则系统会通过类的加载、类的连接、类的初始化三个步骤来对类进行初始化。一般情况JVM是连续完成这三个步骤的,所以三者也统称为类加载或类初始化。
2、类的加载类的加载主要完成三件事:
- 找到类文件,通过全限定类型找到该类的二进制字节流文件(.class文件)。
- 放入方法区,将找到的字节流所代表的静态存储结构转化为方法区中的的运行时数据结构。
- 开个入口,生成一个代表该类的类信息对象(java.lang.Class对象),作为访问方法区的类信息数据的入口。
类的连接主要分为三个阶段:
-
验证阶段:用于检验被加载的类是否有正确的内部结构,并和其他类协调一致。
-
准备阶段:负责为类的类变量分配内存,并设置默认初始化值。
注意:* 静态变量只会给默认值。 * 例:public static int value = 123; // 此时赋给value的值是0,不是123。 * 静态常量(static final修饰的)则会直接赋值。 * 例:public static final int value = 123; // 此时赋给value的值是123。 -
解析阶段:将类的二进制数据中的符号引用替换为直接引用
类的初始化主要根据静态变量的赋值程序为其赋值,因为类的连接阶段只给它赋默认值:
- 类的初始化执顺序是先执行父类初始化语句,在执行本类的。
- 创建类的实例
- 调用类的类方法
- 访问类或者接口的类变量,或者为该类变量赋值
- 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
- 初始化某个类的子类
- 直接使用java.exe命令来运行某个主类
类加载器就是将.class文件加载到内存(方法区)中,并生成对应的类信息对象(Class对象)。见名知意,类加载器就是用来完成上面提到的类加载的。
-
JVM提供了三层类加载器:
-
BootStrap ClassLoader:启动类(根)加载器。主要负责加载核心的类库(java.lang.*等),构造ExtClassLoader和AppClassLoader。
-
ExtClassLoader:扩展类加载器。主要负责加载 jre/lib/ext 目录下的一些扩展的 jar。
-
AppClassLoader:应用程序加载器。主要负责加载应用程序的主函数类
(三个类加载器是继承关系,箭头表示继承)
-
2. 测试类:
public class Test4ClassLoader {
public static void main(String[] args) {
//实例化 -- new
Test4ClassLoader t1 = new Test4ClassLoader();
//反实例化 -- getClass()
Class extends Test4ClassLoader> c1 = t1.getClass();
ClassLoader classLoader = c1.getClassLoader();
System.out.println(classLoader);
ClassLoader father = classLoader.getParent();
System.out.println(father);
ClassLoader grandFather = father.getParent();
System.out.println(grandFather);
}
}
运行结果:
疑惑:为什么没有得到BootstrapClassLoader类加载器?
Boostrap ClassLoader:这个是由c++实现的,所以在方法区并没有Class对象的实例存在
- 类加载器之双亲委派机制
(流程走向:先走蓝线再走红线流程)
流程解释
流程解释:当一个类似Car.class文件要被加载时,不考虑我们自定义的类加载器,先会到 AppClassLoader中检查是否加载过,如果有,那就无需再加载了。如果没有,那么会拿到 父加载器,然后调用父加载器的loadClass方法。父类中同理也会检查自己是否加载过,如 果没有再往上。注意这类似递归的过程,直到到达Bootstrap classLoader之前,都是检查 是否加载过,并不会选择自己去加载。直到Bootstrap classLoader,已经没有父加载器了 ,这时候考虑自己是否能加载,如果自己无法加载,会沉到子加载器去加载,一直到最底 层,如果没有任何加载器能加载,就会抛出ClassNotFoundException。
双亲委派机制优点
防止系统级别的核心类被篡改。因为向上委托,肯定是优先加载系统提供的类,而自定 义的与核心类同名的类只能有应用类加载器加载,不影响其他程序中对核心类的使用。三、拓展 1、自定义类加载器
-
自定义类加载器的使用场景
(1)加密:Java代码可以轻易的被反编译,如果你需要把自己的代码进行加密以防止反编译,可以先将编译后的代码用某种加密算法加密,类加密后就不能再用Java的ClassLoader去加载类了,这时就需要自定义ClassLoader在加载类的时候先解密类,然后再加载。(2)从非标准的来源加载代码:如果你的字节码是放在数据库、甚至是在云端,就可以自定义类加载器,从指定的来源加载类。
(3)以上两种情况在实际中的综合运用:比如你的应用需要通过网络来传输 Java 类的字节码,为了安全性,这些字节码经过了加密处理。这个时候你就需要自定义类加载器来从某个网络地址上读取加密后的字节代码,接着进行解密和验证,最后定义出在Java虚拟机中运行的类。
-
参考博客:
自定义类加载器 - twoheads - 博客园 (cnblogs.com)
自定义类加载器–Tomcat
- 一个对象的实例化过程:
Person p = new Person();
1,JVM会读取指定的路径下的Person.class文件,并加载进内存,并会先加载Person的父类(如果有直接的父类的情况下).
2,在堆内存中开辟空间,分配地址。
3,并在对象空间中,对对象中的属性进行默认初始化。
4,调用对应的构造函数进行初始化。
5,在构造函数中,第一行会先调用父类中构造函数进行初始化。
6,父类初始化完毕后,再对子类的属性进行显示初始化。
7,进行子类构造函数的特定初始化。
8,初始化完毕后,将地址值赋值给引用变量Person p。



