原文链接
类的加载过程
加载
- 通过全类名获取类的二进制字节流
- 将字节流所代表的动态存储构造转化为办法区的运行时数据结构
- 在内存中生成一个代表这个类的
java.lang.Class
对象,作为办法区这个类的各种数据的办法入口
从何处获取字节流:
- 从本地磁盘获取
- 从网络获取
- 运行时计算生成(动静代理)
- 从其余文件生成(JSP 文件)
- 从加密文件中获取
链接
验证
目标:确保 Class 文件的字节流中蕴含的信息合乎《JAVA 虚拟机的》全副束缚要求,保障这些信息被当做代码运行后不会危害虚拟机本身的平安
验证的四个阶段
-
文件格式验证
- 目标是保障输出的字节流可能正确的解析并存储于办法区之内,格局上合乎形容一个 Java 类型信息的要求
-
元数据验证
- 目标是对类的元数据信息进行语义校验,保障不存在与《Java 虚拟机》定义相悖的元数据信息
- 字节码验证
目标是通过数据流剖析和管制剖析,确定程序语义是非法的且合乎逻辑
-
符号援用验证
- 目标是确保解析行为能够失常执行
筹备
- 为类变量分配内存并设置变量初始值的阶段
根本类型的初始值
数据类型 | 零值 |
---|---|
int | 0 |
long | 0L |
short | (short)0 |
char | ”\u0000′ |
byte | (byte)0 |
boolean | false |
float | 0.0f |
double | 0.0d |
reference | null |
解析
目标:Java 虚拟机将常量池内的符号援用替换为间接援用
解析范畴:
- 类和接口的解析
- 字段解析
- 办法解析
- 接口办法解析
初始化
初始化类变量和其余资源。初始化阶段就是执行类结构器 <clinit>()
办法的过程。<clinit>()
是 Java 编译器的自动化产物。
<clinit>()
是由编译器主动收集类中的所有类变量的赋值动作和动态语句块中的语句合并产生的,编译器收集的程序是由在源文件中呈现的程序决定的- 在子类
<clinit>()
办法执行前,父类的<clinit>()
办法会执行结束 - 如果一个类或接口中没有动态语句块或变量赋值操作,编译器不会为这个类生成
<clinit>()
办法
类加载器
疏导类加载器用于加载 Java 外围类库。例如:String.class 和 HashMap.class
疏导类加载器
- 通过 C 或 C ++ 语言实现,嵌套在 JVM 外部
- 不继承
java.lang.ClassLoader
,没有父加载器 - 加载扩大类加载器和利用类加载器
自定义类加载器
- 应用 Java 语言编写
- 存在蕴含关系
启动类加载器(Bootstrap ClassLoader)
- 应用 c /c++ 语言实现,嵌套在 JVM 外部
- 用来加载外围类库
- 不继承子
java.lang.ClassLoader
,没有父加载器 - 加载扩大类和应用程序类加载器,并制订为他们的父类加载器
- Bootstrap 启动类加载器只加载包名为 java、java、sun 等结尾的类
扩大类加载器(ExtClassLoader)
- Java 语言编写
- 派生于 ClassLoader 类
- 父类加载器为 Bootstrap ClassLoader
零碎类加载器(AppClassLoader)
- Java 语言编写
- 派生于 ClassLoader 类
- 父类为 ExtClassLoader
- 负责加载环境变量 classpath 或零碎属性 java.class.path 指定门路下的类
- 是 默认 的类加载器
如何实现本人的类加载器?
- 实现
ClassLoader
类,并重写findClass()
办法
为什么要自定义类加载器?
- 隔离加载类
- 批改类加载的形式
- 扩大加载源
- 避免源码泄露
双亲委派机制
原理:
- 如果一个类加载器收到一个类加载申请,它不会本人先去加载,而是把这个申请委托给父类加载器去执行
- 如果父类加载器还存在父类加载器,则进一步向上委托,顺次递归,申请最终将达到顶层的 Bootstrap ClassLoader
- 如果父类加载器能够实现类加载工作,就胜利返回,若父类加载器无奈实现加载工作,子加载器才会尝试本人去加载
劣势
- 防止类的反复加载
- 爱护程序平安,避免外围 API 被随便篡改
其余
-
在 JVM 中示意两个 class 对象是否为同一个类存在的必要条件
- 类的全类名必须雷同
- 类加载器必须是对立个