虚拟机把Class文件加载到内存,并对其进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是类的加载机制。
类的生命周期
加载
1、通过一个类的全限定名来获取定义此类的二进制字节流。
2、将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
3、在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。
对于数组类而言,数组类本身不通过类加载器创建,它是由Java虚拟机直接创建的。
验证
为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。
准备
准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些变量所使用的内存都在方法区中进行分配。仅分配类变量,即static修饰的变量。这里所说的初始值是数据类型的零值。
解析
解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程。
符号引用:以符号来描述引用的目标。
直接引用:可以直接指向目标的指针、相对偏移量或是能间接定位到目标的句柄。
初始化
才真正开始执行类中定义的Java代码。初始化阶段就是执行类构造器 。
类加载器
类加载器用于实现类的加载动作。比较两个类是否“相等”,只有在这两个类是由同一个类加载器加载的前提下才有意义;否则,即使这两个类来源于同一个Class文件,被同一个虚拟机加载,只要加载它们的类加载器不同,那么这两个类就必定不相等。
类型
启动类加载器(Bootstrap ClassLoader)
这个类加载器是用C++语言实现的。负责加载存放在<JAVA_HOME>\lib目录中的类库,或者被-Xbootclasspath参数指定路径中的,并且是虚拟机识别的类库。顶层类加载器,没有父类加载器。
扩展类加载器(Extension ClassLoader)
这个类加载器是由sun.misc.Launcher$ExtClassLoader实现,它负责加载<JAVA_HOME>/lib/ext目录下或者被java.ext.dirs系统变量所指定位路径中的类库,开发者可以直接使用标准扩展类加载器。它的父类加载器是启动类加载器。
应用类加载器(Application ClassLoader)
这个类加载器是由sun.misc.Launcher$AppClassLoader实现。这个类加载器是ClassLoader中getSystemClassLoader()方法的返回值。它负责加载用户类路径上所指定的库类,开发者可以直接使用这个类加载器。它的父类加载器是扩展类加载器。
自定义类加载器
开发者自己实现的类加载器。它的父类加载器是应用类加载器。
双亲委派机制
意义
防止内存中出现多份同样的字节码。
源码
ClassLoader.loadClass()
1 | protected Class<?> loadClass(String name, boolean resolve) |
ClassLoader.findClass()
1 | protected Class<?> findClass(String name) throws ClassNotFoundException { |
自定义
继承ClassLoader,并实现findClass方法。
打破双亲委派机制
继承ClassLoader,并实现findClass()和loadClass()方法。