在Java中,一个类用其全限定类名(包名和类名)作为标识,在JVM中,一个类用其全限定类名和其类加载器作为其唯一标识。
当JVM启动时,会形成由3个类加载器组成的初始化类加载器层次结构。
1)Bootstrap ClassLoader:根类加载器,非常特殊,它并不是java.lang.ClassLoader的子类,而是JVM自身实现的。根类加载器所加载的核心类库在java Jdk安装路径jdk1.8_45/jre/lib/文件夹里,比如rt.jar,jce.jar,resources.jar等到。因此,我们可以解释一个现象,为什么程序中可以使用String,System这些核心类库?因为这些核心类库都在rt.jar文件中。
2)Extension ClassLoader:拓展类加载器,负责加载JRE的扩展目录(%JAVA_HOME%/jre/lib/ext或者有java.ext.dirs系统属性指定的目录)中的JAR包的类。因此我们可以通过此,为Java拓展核心类库以外的功能了。只需要把自己开发的类打包成JAR文件,然后放入上述的路径即可。
3)System ClassLoader :系统类加载器,负责加载在Java的核心类。负责在JVM启动时紧挨在来自java命令的-classpath选项、java.class.path系统属性,或CLASSPATH环境变量所指定的JAR包和类路径。可以通过ClassLoader的静态方法,getSystemClassLoader()来获取系统类加载器。如果没有特别指定,则用户自定义的类加载器都以系统类加载器作为父加载器。
二、类加载机制
主要是以下3种:
1)全盘负责:当一个类加载器负责加载某个Class时,该Class所依赖的和引用的其他Class也将由该类加载器负责载入,除非显式使用另一个加载器来载入。
2)父类委托:先让父类加载器视图加载该Class,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类。
3)缓存机制:当程序中需要使用某个Class时,类加载器先从缓存区中搜寻该Class,只有当缓存区中不存在该Class对象时,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存入缓存区中。
JVM中4种类加载器的层次结构
根类加载器<------扩展类加载器<------系统类加载器<------自定义加载器
类加载器加载Class大致要经过如下步骤:(结合上述的类加载机制)
1)检测此Class是否载入过,如果有则直接进入返回对应的java.lang.Class对象
2)如果父类加载不存在(如果没有父类加载器,则要么Parent一定是根类加载器要么本身就是根类加载器),则请求使用根类加载器来载入目标类,如果成功载入,则返回对应的Class对象,否则抛出ClassNotFoundException异常。
3)如果父类加载器存在,请求使用父类加载器去载入目标类,如果载入成功则返回对应的Class对象,否则当前类加载器尝试寻找Class文件(从与此ClassLoader相关的类路径中寻找),如果找到了则载入该Class文件,并返回对应的Class对象。如果不成功,则抛出ClassNotFoundException异常
三、创建并使用制定一的类加载器
JVM中除根类加载器之外的所有类加载器(扩展类加载器、系统类就爱在其)都是ClassLoader的子类的实例,通过继承ClassLoader,重写该ClassLoader所包含的Protected方法实现自定义类的加载。
使用自定义ClassLoader可以实现如下常见的功能:
1)执行代码前自动验证数字签名
2)根据用户提供的密码解密代码,从而可以实现代码混淆器来避免反编译Class文件
3)根据用户需求来动态的加载类。
4)根据应用需求把其他数据以字节码的形式加载到应用中
URLClassLoader是ClassLoader的一个实现类,该类也是系统类加载器和拓展类加载器的父类。它可以从本地文件系统获取二进制文件来加载类,也可以从远程主机获取二进制文件来加载类。