步骤
加载-连接-初始化
其中连接又分为验证-准备-解析三步
加载类
- 接收:通过全限定名获取二进制数据流,即通过文件系统读入一个class后缀的文件或解析jar、zip包归档文件得到class文件。还有一些奇葩方法例如将二进制数据放入db通过db读取,或者通过http接收。
- 解析:解析类的二进制数据流为方法区内的数据结构
- 创建:创建Class类实例
类加载器ClassLoader
分类
- BootStrap ClassLoader启动类加载器,由C实现并且在java中没有对象与之对应。用于加载系统核心类,如rt.jar中的java类。特例:java.lang.String类属于系统核心类,因此会被BootStrap 加载器加载,故无法通过getClassLoader 获取对象的累加载器。
- Extension ClassLoader 扩展类加载器,用于加载环境变量%JAVA_HOME%/lib/ext/*.jar包中的java类。
- App ClassLoader 应用/系统类加载器,用于加载用户类。
- 自定义ClassLoader,用于加载一些特殊用途的用户类。
ClassLoader的双亲委派
classLoader会先判断类是否被加载过,如果加载过直接返回,如果没有,会先判断其双亲是否存在,存在则调用双亲的loadClass加载类。不存在,判断使用bootstrap classloader加载,然后还不存在,使用当前classloader加载类。
双亲委派拓展
rt.jar中有些功能,提供了接口,但是需要应用自行实现,例如JDBC驱动。称之为SPI。那么问题是rt.jar是由bootstrap加载器加载的,判断类是否被加载是从自定义加载器到bootstrap类加载器的,所以bootstrap中也无法调用低级别类加载器获取实例,所以这时候需要一个上下文类加载器。在当前线程下存在两个方法,一个set方法,设置当前使用的ClassLoader,一个get,获取上下文ClassLoader。放SPI需要使用应用类加载器加载的类时,则可以通过当前线程传入的AppClassLoader加载外部实现。
打破双亲委派
通过自定义ClassLoader并且重写loadClass方法实现。不过要注意当自定义加载器加微类时,被加载类的默认父类Object有可能无法加载到。因为自定义ClassLoader可能指向的地址不存在Object类信息。还需要使用其他类加载器进行加载。调用其super.loadClass进行Object类的加载。
热替换
java首先是不能像其他需要那样替换了源文件即可实现功能替换的。因为java中虚拟机通过classLoader 将class文件通过二进制流加载到了虚拟机中。
所以只替换class文件是没用的。
那么要做到热更新或者说热替换,就要从classloader入手,通过classloader加载新的源文件。
这里有个重点是:不同的类加载器加载同一个Class文件,在JVM中也不会认为他们是一个类。会分开处理。