上一篇说到了static final,接下来说说类加载
类的加载是指将类的.class文件中的二进制数据读取到内存中,放在了运行时数据区的 方法区,然后在堆区创建一个java.lang.class对象,用来封装类在方法区内的数据结构。类加载的最终产品是位于堆中的class对象,这个对象封装了类在方法区中的数据结构,并提供了访问方法区的数据结构接口。
类的生命周期,加载、验证、准备、解析、初始化五个阶段加载
- 加载 主要是讲class文件中的二进制字节流读到JVM中,主要完成三件事:
- 通过类的全限定名获取到二进制字节流
- 将字节流所代表的静态存储结构转化为方法区的运行时数据结构
- 生成一个对象,作为数据访问入口
- 验证 确保被加载类的正确性。(可以用-Xverifnone参数来关闭大部分的验证)
- 文件格式验证(是否符合Class文件格式规范)
- 元数据验证(是否符合java语言规范)
- 字节码验证(确定程序语义是合法的、符合逻辑的)
- 符号引用验证(确保解析动作能正确执行)
- 准备 为类的静态变量分配内存,并将其初始化为默认值
- 进行内存分配的时候仅包括类变量(static),而不包括实例变量。实例变量会随着对象的实例化分配到堆中
- 数据类型的默认值一般都是0L,0,null等,不是代码中附的值。举个例子,static int value = 3;在准备过后的初始值是0,而把值赋为3的时候是在编译后存放在<clinit>方法中,所以他在初始化的时候才会去赋值(注意:同时被static和final修饰的常量,在声明时必须赋值;被final修饰的可以再声明时赋值也可以在类初始化时赋值,就是必须在调用之前赋值)
- 解析 把类中的符号引用转换为直接应用
- 初始化 为类的静态变量赋予正确的值,jvm负责对类进行初始化,主要对类变量进行初始化。类的初始化时机:
- new
- 访问某个类或接口的静态变量,或对该静态变量赋值
- 反射Class.forname
- 调用类的静态方法
- 初始化某个子类,父类也会被初始化
- java虚拟机启动时被标明为启动类的类,直接使用java.exe来运行某个主类
类的生命周期说完了,接下来说说类的的加载器,有三种
- BootstropClassLoader:负责加载jdk安装目录下jre/lib下,或者-Xbootclasspath参数指定的路径中的,并且能被虚拟机识别的类库,所有的java.*开头的类都会被加载
- ExtensionClassLoader:负责加载jdk安装目录下jre/lib/ext目录中,或者javax.*开头的类,开发者可以直接扩展类加载器
- ApplicationClassLoader:就好比引用的第三方包,加载的是classpath下的包