什么是类的加载
类的加载是指将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,并且在堆中创建对应的java.lang.class对象,用来封装类在方法区的数据结构,并且提供访问方法区内数据结构的接口。
类的生命周期
类的生命周期包含加载,验证,准备,解析,初始化,使用,卸载这几个方面。其中从加载到初始化,都属于类的加载。
类的加载过程
加载(虚拟机主要完成三件事)
①通过一个类的全限定名来获取其二进制字节流
②将字节流所代表的静态存储结构转化为方法区的运行时数据结构
③在堆中生产java.lang.class对象,用于对方法区中数据的访问接口
ps:加载阶段获取二进制字节流的动作是可控性最强的阶段,因为开发人员既可以使用系统提供的类加载器加载,也可以使用自定义的类加载器完成加载
验证阶段
①文件格式验证:检验一系列的文件格式,版本是否虚拟机可以处理
②元数据验证:对字节码描述的信息进行语义分析
③字节码验证:通过数据流和控制流的分析,确定语义是否合法,逻辑是否正确
④符号引用验证:确保解析动作能正确执行
准备阶段
为类的静态变量分配内存,并将其初始化为默认值(仅为其分配内存,不会把真实的变量值赋值给它)
解析
把类中的符号引用转化成直接引用
初始化
这一步才是JVM为变量赋予正确的初始值
①如果这个类没有被加载和链接,则程序先加载并连接这个类
②如果该类的直接父类没有被初始化,则先初始化直接父类
③如果类中有初始化语句,则系统依次执行这些语句
到了这一步一个类的加载基本完成了,可以对类进行使用了
一个类的加载离不开类加载器,下面就来介绍一下
类加载器
下面是几种类加载器的层次关系
启动类加载器BootStrap ClassLoader
有初始用来加载java的核心库(JAVA_HOME/jre/lib/rt.jar,或sun.boot.class.path路径下的内容),是用原生代码来实现的(C实现的),并不继承自java.lang.ClassLoader。加载扩展类和应用程序类加载器,并指定它们的父类加载器。
扩展类加载器ExtClassLoader
用来加载java的扩展库(JAVA_HOME/jre/lib/ext/*.jar,或java.ext.dirs路径下的内容)java虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载java类。由sun.miscLauncher$ExtClassLoader实现,继承自java.lang.ClassLoader
应用类加载器AppClassLoader
它根据java应用的类路径(classpath,java.class.path路径)来加载指定路径的类,一般来说,java应用的类都是由它来完成加载的。由sun.misc.Launcher$AppClassLoader实现,继承自java.lang.ClassLoader
自定义类加载器UserClassLoader
开发人员可以通过继承java.lang.ClassLoader类的方式实现自己的类加载器,以满足一些特殊的需求。
类的加载机制
①全盘负责:当一个类加载器负责加载某个class时,该class所依赖的其他class也将由该类加载器加载。
②双亲委派机制(父类委托):先让父类加载器试图加载该类,只有当父类加载器无法加载该类的时候,才会尝试自己加载,如果加载不了,抛出异常
③缓存机制:加载过的class对象都会被缓存,当需要使用某个class时,类加载器先从缓存区域寻找,如果不存在,再去加载(ps:因为这个原因,每当我们修改代码时,总是得重新启动才会生效)