一个类的整个生命周期如下:
.class文件
需要加载进虚拟机中,才能运行和使用这些.class文件
。加载的过程,就是把.class文件
加载到内存,根据.class文件
在内存中创建Class对象
,并对数据进行校验、解析、初始化,最终形成虚拟机可以直接使用的Java
类型,这也是Java
虚拟机的类加载机制。
类加载具体分为3个步骤,如下:
-
加载:查找字节码,并在内存中创建一个
Class对象
。 -
连接:验证类中的字节码,为新对象分配内存,并将变量的值设置为系统默认的初始值。
-
初始化:按照程序员的意愿进行初始化。
类加载是由类加载器完成的,选择用哪个类加载器来加载类是使用双亲委派机制来确定的。
小纸条
数组类型不通过类加载器创建,它由 Java
虚拟机直接创建。
1. 类加载过程
上图中黄色背景中的部分是类加载所需要进行的操作。
2. 什么时候会加载类
第一次使用到类的时候(比如创建类的实例、要加载子类前需要先加载父类、用到类的静态方法等)会加载类,不使用不会提前加载。
3. 用什么加载类?-类加载器
JVM
中内置了三个重要的 ClassLoader
,除了 BootstrapClassLoader
其他类加载器均由 Java
实现且全部继承自java.lang.ClassLoader
:
-
BootstrapClassLoader(启动类加载器) :最顶层的加载类,由
C++
实现,负责加载%JAVA_HOME%/lib
目录下的 jar 包和类或者被-Xbootclasspath
参数指定的路径中的所有类。 -
ExtensionClassLoader(扩展类加载器) :主要负责加载
%JRE_HOME%/lib/ext
目录下的jar
包和类,或被java.ext.dirs
系统变量所指定的路径下的jar
包。 -
AppClassLoader(应用程序类加载器) :面向我们用户的加载器,负责加载当前应用
classpath
下的所有jar
包和类。
4. 选择类加载器的规则-双亲委派机制
4.1 双亲委派模型
每一个类都有一个对应它的类加载器。系统中的类加载器在协同工作的时候会默认使用 双亲委派模型。
-
判断。在类加载的时候,系统会首先判断当前类是否被加载过。已经被加载的类会直接返回,否则才会尝试加载。
-
向上委派。加载的时候,首先会把该请求委派给父类加载器的
loadClass()
处理,因此所有的请求最终都应该传送到顶层的启动类加载器BootstrapClassLoader
中。 -
向下加载。当父类加载器无法处理时,才由自己来处理。当父类加载器为 null 时,会使用启动类加载器
BootstrapClassLoader
作为父类加载器。
4.2 好处
-
防止加载同一个
.class
文件 -
保证核心
API
不被篡改
双亲委派模型保证了 Java
程序的稳定运行,可以防止加载同一个 .class
文件,避免类的重复加载。也保证了 Java
的核心 API 不被篡改。
小纸条
如何判断两个 class 是否相同??
JVM
在判定两个class
是否相同时,不仅要判断两个类名是否相同,而且要判断是否由同一个类加载器实例加载的
4.3 如何打破双亲委派
如果我们想自己写一个类加载器来加载这个类,而不是通过系统默认的类加载器来加载的话。就要自定义一个类加载器,重写loadClass()。双亲委派机制是通过类加载器 ClassLoader
中的 loadClass()
实现的。
-
在自定义类加载器的时候,如果不想打破双亲委派,只重写
findClass()
。 -
如果想打破双亲委派,重写
loadClass()
。
典型的打破双亲委派的框架和中间件是 Tomcat
和 osgi
。