————————————————————————————————
类加载以及类加载的过程
类加载机制就是虚拟机把类的数据从Class文件加载到内存,对其进行转换解析,最后形成可以被虚拟机直接使用的Java类型。
以下情况触发类加载:
-
new 一个实例
-
访问static变量方法
-
new子类会加载父类
- class.forName() 反射
-
虚拟机启动时,定义的main方法的类会首先触发类加载
类加载的过程
加载、验证、准备、初始化和卸载这5个阶段的顺序是确定的,而解析阶段则不一定:它在某些情况下可以在初始化阶段之后再开始,这是为了支持Java语言的运行时绑定(也称为动态绑定)。
加载、验证、准备、解析和初始化
①:加载,通过全类名获取定义类的二进制字节流,将字节流所代表的静态存储结构转换为方法区的运行时数据结构,在内存中生成一个代表该类的 Class 对象,作为方法区这些数据的访问入口。
②:验证,文件格式验证(判断是否是Class文件格式),元数据验证(对字节码进行语义分析),字节码验证(验证程序语义的合法性),符号引用验证(确保解析正确完成)
③:准备,主要是将类变量(被static修饰符修饰)在方法区进行内存分配并进行初始化。
④:解析,对类、方法、字段、接口进行解析,(常量池中的符号引用)将符号引用转化为直接引用。
- 符号引用:仅仅是一串符号,并不知道该引用对应的内存地址;不知道对应的是方法还是对象
- 直接引用:此时该引用已经有对应的内存地址了
⑤:初始化
初始化就是类加载的最后一个阶段了。准备阶段变量可能已经被赋过初值了,初始化的时候会根据程序代码的要求来对类里的一些变量和数据进行操作。也可以说就是在调用<clinit()>方法
会触发初始化的场景:
- main 方法所在的类,总会被首先初始化
- 首次访问这个类的静态变量或静态方法时
- 子类初始化,如果父类还没初始化,会优先引发父类的初始化
- 子类访问父类的静态变量,只会触发父类的初始化
- Class.forName
- new 会导致初始化
不会触发初始化的场景
- 访问类的 static final 静态常量(基本类型和字符串)不会触发初始化
- 访问这些数据是在类链接的准备阶段就完成了
- 访问类对象.class 不会触发初始化(加载阶段完成)
- 创建该类的数组不会触发初始化
- 类加载器的 loadclass 方法
- Class.forName 的参数2设置为 false 时
类加载器
通过一个类的全限定名来获取类的二进制字节流,实现这个动作的代码被称为“类加载器”(Class Loader)。
比较两个类是否“相等”,只有在这两个类是由同一个类加载器加载的前提下才有意义,否则,即使这两个类来源于同一个Class文件,被同一个Java虚拟机加载,只要加载它们的类加载器不同,那这两个类就必定不相等。
- 启动类加载器:用来加载java核心类库,无法被java程序直接引用;
- 扩展类加载器:用来加载java的扩展库,java的虚拟机实现会提供一个 扩展库目录,该类加载器在扩展库目录里面查找并加载java类;
- 系统类加载器:它根据java的类路径来加载类,一般来说,java应用的类都是通过它来加载的;
- 自定义类加载器:由java语言实现,继承自ClassLoader;