什么是类加载
我们写的java源文件先被编译成.class字节码文件,然后由类加载器加载到JVM内存中,加载完成以后就会得到一个Class对象,我们就可以使用new关键字来实例化这个对象。
加载过程
装载过程主要分为 加载、连接(验证、准备、解析)、初始化
三个阶段
每个阶段的主要作用:
1、加载:查找和载入Class文件
2、链接,分为以下三个小阶段
- 验证:验证载入的class文件数据的正确性
- 准备:给
类变量
分配存储空间,并设置默认初始值0 - 解析:将
运行时常量池
内的符号引用
转换为直接引用
的过程(符号引用是用一组符号描述所引用的目标;直接引用是指向目标的指针)
3、初始化:对静态变量,静态代码块执行初始化工作
学习了上面的知识之后,我们就可以解释为什么全局变量不用初始化而局部变量需要初始化了
类变量在链接的准备阶段会被默认赋值,然后在初始化阶段会被赋予实际的值
比如int a=1;
a在准备阶段=0,在初始化阶段=1
而局部变量表不存在系统初始化的过程,所以在使用前必须进行显式赋值
类加载器
类加载器就是负责进行类加载的装置
JVM在运行的时候,会产生3个类加载器,这三个类加载器组成了一个层级关系
每个类加载器分别去加载不同作用范围的jar包,比如
-
Bootstrap ClassLoader,主要是负责Java核心类库的加载,也就是 %{JDK_HOME}\lib下的rt.jar、resources.jar等
-
Extension ClassLoader,主要负责%{JDK_HOME}\lib\ext目录下的jar包和class文件
-
Application ClassLoader,主要负责当前应用里面的classpath下的所有jar包和类文件
除了系统自己提供的类加载器以外,还可以通过ClassLoader类实现自定义加载器,去满足一些特殊场景的需求。
为什么要自定义类加载器?
自定义加载逻辑
有时我们不一定是从类文件中读取类,可能是从网络的输入流中读取类,这就需要做一些加密和解密操作,这就需要自定义类加载器来实现加载类的逻辑。
隔离加载类
两个不同的应用程序可能会依赖同一个第三方类库的不同版本
,为了保证两个版本的类都可以正常被加载,需要利用自定义类加载器加载不同版本的类,以此实现类的隔离加载。
双亲委派机制
双亲委派机制
- 如果一个类加载器收到了类加载请求,它并不会自己去加载,而是先把这个请求委托给父类的加载器去执行;
- 如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归向上,请求最终将到达顶层的
启动类加载器
; - 如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,依次递归向下,这就是双亲委派模式。
为什么要有双亲委派机制?
1、避免类的重复加载:
Java类随着它的类加载器一起具备了一种带有优先级的层次关系,通过这种层级关系可以避免类的重复加载,当父加载器加载之后,子加载器就不会重复加载了、
2、防止核心API被随意篡改:
当自定义一个java.lang.Integer的类,通过双亲委托模式传递到启动类加载器,而启动类加载器发现该类已被加载,并不会重新加载自定义的java.lang.Integer,这样便可以防止核心API库被随意篡改。
那我非要自定义Integer类呢?
可以,但需要用自定义的类加载器去加载,因为系统的类加载器只加载jar包中的那个java.lang.Integer
打破双亲委派机制
如何打破呢?
只要我加载类的时候,不是从 Aapplication ClassLoader->Extension ClassLoader->BootStrap ClassLoader 这个顺序找,那就算是打破了
所以只要我自定义个ClassLoader,重写loadClass方法,方法中实现逻辑不依照往上开始寻找类加载器,那就算是打破双亲委派机制了。
有哪个场景破坏了双亲委派机制?——最典型的就是Tomcat
假设我现在有两个Web应用程序,它们都有一个类,叫做User,并且它们的类全限定名都一样,比如都是com.yyy.User。但是他们的具体实现是不一样的
那么Tomcat是如何保证它们是不会冲突的呢?
答案就是,Tomcat给每个 Web 应用自定义一个类加载器实例(WebAppClassLoader),该加载器重写了loadClass方法,优先加载当前应用目录下的类,如果当前找不到了,才一层一层往上找,这样就做到了Web应用层级的隔离
两个class对象相同的必要条件
在JVM中表示两个class对象是否为同一个类存在两个必要条件:
- 类的
完整类名
必须一致。 - 加载这个类的
类加载器
必须相同。
即:在JVM中,即使这两个类对象来源于同一个Class文件,被同一个虚拟机所加载,但只要加载它们的类加载器不同,那么这两个类对象也是不相等的。