啊啊啊 不想学习 那就来写一篇博文吧┭┮﹏┭┮
有没有听说过Java的类加载器呢??
在了解Java类加载器之前,你是否知道类加载机制呢?
1.初识类加载过程
-
首先在桌面上创建一个txt文件,里面写上一段正确的java代码,保存
-
接下来,将.txt文件命名为.java文件
-
cmd下打开文件所在位置 执行命令javac
javac ceshi.java //进行编译,生成.class文件
- 在执行命令java
java ceshi //类加载过程
在进行java ceshi时,就是进行类加载的过程,也就是把生成的字节码文件装载到java虚拟机(JVM)中。
2.类加载机制
当你编写了一段代码后,需要用JVM对其进行加载。JVM对java类的加载流程:
- 将Java文件编译生成字节码文件
- 类加载器读取字节码文件,并转换成java.lang.Class对象
- 有了该Class对象,JVM利用反射的方法创建对象实例
Java提供了许多不同的类加载器,他们用来加载不同的文件。绝大多数继承于ClassLoader类。
问题来了:这些可爱的类加载器,它们怎么知道自己加载的文件是不是字节码文件呢??
这就需要我们来了解一下字节码文件
执行命令javap -verbose ceshi
下面是显示的结果:
C:\Users\admin\Desktop>javap -verbose ceshi
Classfile /C:/Users/admin/Desktop/ceshi.class
Last modified 2021-5-10; size 415 bytes
MD5 checksum 1c50e1ae93498831a1acd435817d8ed4
Compiled from “ceshi.java”
public class ceshi
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #6.#15 // java/lang/Object.""😦)V
#2 = Fieldref #16.#17 // java/lang/System.out:Ljava/io/PrintStream;
#3 = String #18 // hello,world
#4 = Methodref #19.#20 // java/io/PrintStream.println:(Ljava/lang/String;)V
#5 = Class #21 // ceshi
#6 = Class #22 // java/lang/Object
#7 = Utf8
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 main
#12 = Utf8 ([Ljava/lang/String;)V
#13 = Utf8 SourceFile
#14 = Utf8 ceshi.java
#15 = NameAndType #7:#8 // “”😦)V
#16 = Class #23 // java/lang/System
#17 = NameAndType #24:#25 // out:Ljava/io/PrintStream;
#18 = Utf8 hello,world
#19 = Class #26 // java/io/PrintStream
#20 = NameAndType #27:#28 // println:(Ljava/lang/String;)V
#21 = Utf8 ceshi
#22 = Utf8 java/lang/Object
#23 = Utf8 java/lang/System
#24 = Utf8 out
#25 = Utf8 Ljava/io/PrintStream;
#26 = Utf8 java/io/PrintStream
#27 = Utf8 println
#28 = Utf8 (Ljava/lang/String;)V
{
public ceshi();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object.""😦)V
4: return
LineNumberTable:
line 1: 0
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3 // String hello,world
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 3: 0
line 4: 8
}
SourceFile: “ceshi.java”
以上就是字节码一个简单的字节码文件的全部内容了,我们来看看第四行:
MD5 checksum 1c50e1ae93498831a1acd435817d8ed4
有没有听说过魔数??字节码文件的前四个字节,我们称之为魔数。它的作用,就是来告诉类加载器:黑,我是一个字节码文件,你可以放心加载我了。而MD5,就是用来对魔数进行加密,加密的结果,就是MD5 checksum后面跟着的一长串令人头疼的字符。
好家伙,问题又来了,识别出一个文件是字节码文件了,那么有这么多类加载器,由哪一种类加载器对这个字节码文件进行加载呢?
这就需要来了解类加载器了。
3.类加载器
主要介绍三种类加载器:Bootstrap ClassLoader (启动类加载器)、Ext ClassLoader(扩展类加载器)、App ClassLoader(应用类加载器)
(1)Bootstrap ClassLoader(启动类加载器|根加载器|引导类加载器)
- 用来加载JAVA_HOME/jre/lib里的jar包(该目录下所有jar包是运行JVM时所必需的jar包)
- 类加载器本身也是类,需要有其他类加载器对其进行加载,Bootstrap ClassLoader就是他们的老大(类加载器的顶级父类)。
- Bootstrap ClassLoader是由C语言进行开发的。
- 如果一个类的类加载器是Bootstrap ClassLoader,那么该类的getClassLoader()方法返回null
看来String类是由Bootstrap ClassLoader进行加载的(●’◡’●)
ps:如果你不知道你的JAVA_HOME的位置,你可以在cmd下输入
echo %JAVA_HOME%
进行查看
(2)扩展类加载器(Ext ClassLoader)
- 主要加载JAVA_HOME/jre/ext下的jar包
(3)应用类加载器(App ClassLoader)
- 主要加载用户自己编写的类,也就是CLASSPATH下的所有jar文件
4.双亲委派模型
说实话,我一直对双亲委派模型有一种朦胧的感觉,我怕我说错,就不说了(百度会搜到很多解释)。我来说一下双亲委派模型的好处(●’◡’●)
(1)安全性。可以避免用户自定义的类动态替换java的一些核心类,保证了安全。
(2)避免类的重复加载。JVM判断两个类是否是同一个类,不仅根据类名判断,还根据加载这个类的类加载器是否相同进行判断。用两种类加载器去加载同一个类,得到的就是两个不同的类。