类加载器的作用是 加载class文件到内存中,加载的类的信息存放于方法区,此外,方法区还存放运行时常量池信息。
JVM提供的类加载器有三种:
引导类加载器(Bootstrap ClassLoader): C++ 实现,加载jre/lib/rt.jar目录下的核心库,包括基本数据类型
扩展类加载器(Ext ClassLoader): 加载java扩展库jre/lib/ext/*.jar 或 由 -Djava.ext.dirs命令 指定目录的class文件
系统类加载器(App ClassLoader):又叫应用类加载器,用于加载classpath指定的目录,一般都是我们自己编写的class文件
除以上三种类加载器之外,我们还可以自定义类加载器类对某些类型进行定制化的加载,比如加载一些经过加密的class文件, 在类加载的时候对class进行解密。实现自定义类加载器,只需要继承ClassLoader类, 重写findClass()即可。
代码验证三种类加载器:
public class ClassLoaderTest {
public static void main(String[] args) throws ClassNotFoundException {
// String类 由 引导类加载器加载
ClassLoader c1 = String.class.getClassLoader();
System.out.println(c1);
// TickCalculation类 由 扩展加载器加载
ClassLoader c2 = TickCalculation.class.getClassLoader();
System.out.println(c2);
// ClassLoaderTest类 由 系统类加载器加载
ClassLoader c3 = ClassLoaderTest.class.getClassLoader();
System.out.println(c3);
}
}
运行结果:
null
sun.misc.Launcher$ExtClassLoader@677327b6
sun.misc.Launcher$AppClassLoader@58644d46
Process finished with exit code 0
从运行结果可以看出,在 java代码中获取无法获取引导类加载器, 获取结果为null。原因是引导类加载器不是由java代码实现的,而是由c++代码实现的。
类加载过程:
类的加载过程是类加载系统在将类的字节码文件加载到内存中到类被真正使用之前的过程。类的加载过程总体上分为三步:
加载阶段:将class文件的二进制字节流加载到内存中,并且创建一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。类加载器就是工作在这个阶段。
链接阶段:
验证: 校验被加载的类的内部结构是否正确,主要包括四种:文件格式验证,元数据验证,字节码验证,符号引用验证
准备:为静态变量分配内存,设置默认初始值(零值)
注意: 1.如果变量被final修饰,则在编译时进行分配,准备阶段进行显示初始化
2.不会为实例变量分配初始化,因为静态变量是存在于方法区中的,而实例变量会随着Java对象一起分配到java堆中
解析:将符号引用转换为直接引用,将指针指向内存地址。 PS:解析操作一般在初始化阶段完成以后执行
初始化阶段:执行类构造器方法clinit(),为静态变量赋值
经历这三个阶段后,class文件就可以被实例化了。