有两种类型的类加载器
- Java虚拟机自带的加载器
- 根类加载器(Bootstrap)
- 扩展类加载器(Extension)
- 系统(应用)类加载器(System)AppClassLoader
- 用户自定义的类加载器
- Java.lang.ClassLoader的子类
- 用户可以定制类的加载方式
类加载器并不需要等到某个类被主动使用时再加载它
JVM规范允许类加载器再预料某个类将要被使用时就预先加载它,如果再预先加载的过程中遇到了.class文件缺失或者存在的错误,类加载器必须再程序首次主动使用该类时报告错误(LinkageError错误)
如果这个类一直没有被程序主动使用,那么类加载器就不会报告错误
JVM类的初始化步骤
- 假如这个类还没有被加载和连接,那就先进行加载和连接
- 假如类存在直接父类,并且这个父类还没有被初始化,那么就先初始化直接父类
- 假如类中存在初始化语句,那就依次执行这些初始化语句
双亲委托模型:
- 当一个类被使用时会先去找到系统类加载器AppClassLoader是否存在。如果存在则返回,不存在则找到它的父加载器ExtClassLoader进行查找是否存在。如果存在则返回,不存在则在去找父加载器BootstrapClassLoader进行查找。如果存在则返回,不存在则由BootstrapClassLoader根类加载器尝试加载。成功则返回,失败则退回子加载器ExtClassLoader去尝试加载。成功则返回,失败则由子类加载器AppClassLoader进行加载,成功则返回,失败报错。
若有一个类加载器能够成功加载Test类,那么这个类加载器被称为定义类加载器,所有能够成功返回Class对象引用的类加载器(包括定义类加载器)都被成为初始类加载器
* <p> {@code Class} objects for array classes are not created by class
* loaders, but are created automatically as required by the Java runtime.
* The class loader for an array class, as returned by {@link
* Class#getClassLoader()} is the same as the class loader for its element
* type; if the element type is a primitive type, then the array class has no
* class loader.
注意数组类型并不是通过类加载器加载得到,而是通过JVM动态创建的,当数值类调用getClassLoader(),将获得数值类型的类加载器,当数组的类型是原始类型则没有类加载器。
例:
{
String[] strings = new String[5];
strings.getClass().getClassLoader(); //结果为null;
MyTest[] mytests = new MyTest[2];
mytest.getClass().getClassLoader(); //结果为AppClassLoader
}
类加载器的命名空间
- 每个类加载器都有自己的命名空间,命名空间由该加载器及所有的父加载器的类组成。
- 再同一个命名空间中,不会出现类的完整名字(包括类的包名)相同的两个类
- 在不同的命名空间中,有可能会出现类的完整名字(包括类的包名)相同的两个类
注意父加载器加载的类无法访问子加载器加载的类,子加载器能够访问父加载器加载的类
类的卸载
- 当MySample类被加载、连接和初始化后,它的生命周期就开始了。当代表MySample类的Class对象不再被引用,即不可触及时,Class对象就会结束生命周期,MySample类在方法区内的数据也会被卸载,从而结束Sample类的生命周期
- 一个类何时结束生命周期,取决于代表它的Class对象何时结束生命周期
- 由Java虚拟机自带的类加载器加载的类,在虚拟机的生命周期中,始终不会被卸载。包括根类加载器、扩展类加载器和系统类加载器。Java虚拟机本身会始终引用这些类加载器,而这些类加载器则会始终引用它们所加载的Class对象,因此这些Class对象始终是可触及的
- 由用户自定义的类加载器所加载的类是可以被卸载的
类双亲委托模型的好处:
- 可以确保java核心库的类型安全:所有的Java应用都至少会引用java.lang.Object这个类会被加载到java虚拟机中;如果这个加载过程由java应用自己的类加载器所完成的,那么很可能就会在JVM中存在多个版本的java.lang.Object,而这些类之间还是不兼容不可见的(正式命名空间在发挥作用)。借助于双亲委托机制,java核心类库中的类的加载工作都是由启动类(根类)加载器统一完成的,从而确保了java应用中确保了java所使用的都是统一版本的java核心类库,他们都是兼容的
- 确保java核心类库提供的类不会被自定义类所替代
- 不同的类加载器可以为相同名称(binary name)的类创建额外的命名空间,相同名称的类可以并存在java虚拟机中只需要通过不同的类加载器加载即可,不同类加载器所加载的类之间是不兼容的,这就相当于java虚拟机内部创建了一个又一个相互隔离的java类空间,这个技术在很多框架中得到了实际应用