思考类加载过程
思考类加载过程
什么是类加载?
类加载是一个将.class
字节码文件实例化成Class
对象并进行相关初始化过程。
或者负责读取 Java 字节代码,并转换成java.lang.Class
类的一个实例。
说说类的加载过程?
Java
的类加载器是一个运行时核心基础设施模块,主要在启动进行类的Load
,Link
和Init
,即加载,链接,初始化。
加载(Load): **读取类文件产生二进制流,**并转化特定数据结构,(Class文件、ZIP包、运行时计算生成的)
**链接 (Link)**包括(校验、准备、解析)三个步骤.
- 校验:是更为详细的校验,确保Class文件的字节流中包含的信息是否符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。(详细、安全)
- 准备:正式为类变量分配内存并设置类变量的初始值阶段,即在方法区中分配这些变量所使用的内存空间。
- 解析:解析阶段是指虚拟机将常量池中的符号引用替换为直接引用的过程。(可以在初始化后进行)
初始化(Init): 执行类构造器 方法,如果赋值运算是通过其他类的静态方法完成的,那么会马上解析另一个类,在虚拟机栈中执行完毕后返回值进行赋值
ClassLoader的作用是啥?
ClassLoader
的使用是提前加载.class
类文件存在内存中。
类加载器种类
-
启动类加载器,Bootstrap ClassLoader,加载JAVA_HOME\lib,或者被-Xbootclasspath参数限定的类
-
扩展类加载器,Extension ClassLoader,加载\lib\ext,或者被java.ext.dirs系统变量指定的类
-
应用程序类加载器,Application ClassLoader,加载ClassPath中的类库
-
自定义类加载器,通过继承ClassLoader实现,一般是加载我们的自定义类
类加载器间的关系
我们进一步了解类加载器间的关系(并非指继承关系,而是组合),主要可以分为以下4点
启动类加载器,由C++实现,没有父类。
拓展类加载器(ExtClassLoader),由Java语言实现,父类加载器为null
系统类加载器(AppClassLoader),由Java语言实现,父类加载器为ExtClassLoader
自定义类加载器,父类加载器肯定为AppClassLoader。
面试官:那自己怎么去实现一个ClassLoader呢?请举个实际的例子
自己实现ClassLoader时只需要继承ClassLoader类,然后覆盖findClass(String name)方法即可完成一个带有双亲委派模型的类加载器。
自定义类加载器
在JDK1.2之前,在自定义类加载时,总会去继承ClassLoader类并重写loadClass方法,从而实现自定义的类加载类,但是在JDK1.2之后已不再建议用户去覆盖loadClass()方法,而是建议把自定义的类加载逻辑写在findClass()方法中,
如果要符合双亲委派规范,则重写findClass方法(用户自定义类加载逻辑);
要破坏的话,重写loadClass方法(双亲委派的具体逻辑实现)。
双亲委派模型
我们可以由图看到以下过程:
- 自底向上检查类是否已经加载,若已加载,直接返回。
- 若所有父类都没有加载该类,则自顶向下尝试加载该类。
- 如果加载不成功,则抛出ClassNotFoundException异常。
双亲委派好处
-
避免同一个类被多次加载;
比如加载位于rt.jar包中的类java.lang.Object,不管是哪个加载器加载这个类,最终都是委托给顶层的启动类加载器进行加载,这样就保证了使用不同的类加载器最终得到的都是同样一个Object对象。
-
每个加载器只能加载自己范围内的类;