类加载器深入解析及重要特性剖析
- 过程
- 加载:就是把二进制形式的java类型读入到java虚拟机中
- 验证:验证class文件的正确性
- 准备:为类分配内存,设置默认值。但是在到达初始化之前,类变量都没有初始化为真正的初始值。
- 解析:解析过程就是在类型的常量池中寻找类、接口、字段和方法的符号引用,把这些符号引用替换成直接引用的过程。
- 初始化:为类变量赋予正确的初始值
- 类实例化:
- 为新的对象分配内存
- 为实例变量赋予默认值
- 为实例变量赋正确的初始值
- java编译器为它编译的每一个类都至少生成一个实例初始化方法,在java的class文件中,这个实例化初始化方法被称为""。针对源代码中每一个类的构造方法,java编译器都产生一个方法
- 垃圾回收和对象终结
1.类的加载
类的加载的最终产品是位于内存中的Class对象。
Class对象封装了类在方法区内的数据结构,并且向Java程序员提供了访问方法区内的数据结构的接口。
-
两种类型的类加载器:
-
Java虚拟机自带的加载器
- 根类加载器(Bootstrap)
- 扩展类加载器(Extension)
- 系统(应用)类加载器(System)
-
用户自定义的类加载【除了上面提到的三种虚拟机自带的类加载】
- java.lang.ClassLoader的子类
- 用户可以定制类的加载方式 【即所有用户自定义的类加载器都应该继承ClassLoader】
- 上图看着是一种继承关系,实际上是一种包含关系。
-
-
类加载器并不需要等到某一个类被”首次主动使用“时再加载它。下面两方面是其原因:
- JVM规范允许类加载器在预料某一个类将要被使用时就预先加载它,如果在预先加载的过程中遇到了.class文件缺失或存在错误,类加载器必须在程序首次主动使用该类时才报告错误(LinkageError错误)。
- 如果这个类一直没有被程序主动使用,那么类加载器就不会报告错误。
2.类的验证
- 类被加载后,就进入连接阶段。连接就是将已经读入到内存的类的二进制数据合并到虚拟机的运行时环境中去。
- 类的验证的内容
- 类文件的结果检查
- 语义检查
- 字节码验证
- 二进制兼容性的验证
3.类的准备
-
在准备阶段,Java虚拟机为类的静态变量分配内存,并设置默认值的初始值。例如对于以下Sample类,在准备阶段,将为int类型的静态变量a分配4个字节的内存空间, 并且赋予默认值0,为long类型的静态变量b分配8个字节的内存空间,并赋予默认值0。0
public class Sample{ private static int a=1; public static long b; static{ b=2; } ... }
4.类的初始化
-
在初始化阶段,java虚拟机执行类的初始化语句,为类的静态变量赋予初始值。在程序中,静态变量的初始化有两种途径:
- (1)在静态变量的声明处进行初始化;
- (2)在静态代码块中进行初始化。
例如一下代码中,静态变量a和b都被显示初始化,而静态变量c没有显示初始化,它将保持默认值0.
public class Sample{ private static int a=1; //在静态变量的声明处进行初始化 public static long b; public static long c; static{ b=2; //在静态代码块中进行初始化 } ... }
-
静态变量的声明语句,以及静态代码块都被看做类的初始化语句,java虚拟机会按照初始化语句在类文件中的先后顺序来依次执行他们,例如当一下Sample类被初始化后,他的静态变量a的取值为4。
public class Sample{ static int a = 1; static{ a=2; } static{ a=4; } public static void main(String[] args){ System.out.println("a="+a); //打印a=4 } }
-
类的初始化步骤
- 假如这个类还没有被加载和连接,那就先进行加载和连接;[初始化是在加载和连接之后]。
- 假如类存在直接父类,并且这个父类还没有被初始化,那就先初始化直接父类。
- 假如类中存在初始化语句,那就按照这些语句出现的先后顺序依次执行这些初始化语句。
5.类的初始化时机
- 首先是找 类的主动使用 7种方式, 【位置:第五节 5-2】
- 当java虚拟机初始化一个类时,要求它的所有父类都已经被初始化,但是,这条规则并不适用于接口
- 在初始化一个类时,并不会先初始化它所实现的接口。
- 在初始化一个接口时,并不会先初始化它的父接口。
- 因此,一个父类接口并不会因为它的子接口或者实现类的初始化而初始化。只有当程序首次使用特定接口的静态变量时,才会导致该接口的初始化。
- 只有当程序访问的静态变量或静态方法确实在当前类或当前接口中定义时,才可以认为是对类或者接口的主动使用。
- 调用ClassLoader类的loadClass方法加载一个类,并不是对类的主动使用,不会导致类的初始化。
6.类加载器
- 类加载器用来把类加载到java虚拟机中,从JDK1.2版本开始,类的加载过程采用父亲(双亲)委托机制,这种机制能够更好的保证java平台的安全,在此委托机制中,除了java虚拟机自带的根类加载器以外,其余的类加载器有且只有一个父加载器。
- 当java程序请求加载器loader1加载Sample类时,loader1首先委托自己的父加载器去加载Sample,若父加载器能加载,则由父加载器完成加载任务,否则,才由加载器loader1本身加载Sample类。