JAVA类装载方式
1.隐式装载, 程序在运行过程中当碰到通过new 等方式生成对象时,隐式调用类装载器加载对应的类到jvm中
2.显式装载, 通过class.forname()等方法,显式加载需要的类
Java程序启动时,并不是一次把所有的类全部加载后再运行,它总是先把保证程序运行的基础类一次性加载到jvm中,其它类等到jvm用到的时候再加载
Java语言系统自带有三个类加载器:
- Bootstrap ClassLoader 最顶层的加载类,Bootstrp加载器是用C++语言写的,它是在Java虚拟机启动后初始化的,主要加载核心类库,%JRE_HOME%\lib下的rt.jar、resources.jar、charsets.jar和class等。另外需要注意的是可以通过启动jvm时指定-Xbootclasspath和路径来改变Bootstrap ClassLoader的加载目录。比如java -Xbootclasspath/a:path被指定的文件追加到默认的bootstrap路径中。我们可以打开我的电脑,在上面的目录下查看,看看这些jar包是不是存在于这个目录。
- Extention ClassLoader 扩展的类加载器,ExtClassLoader是用Java写的,具体来说就是 sun.misc.Launcher$ExtClassLoader,加载目录%JRE_HOME%\lib\ext目录下的jar包和class文件。还可以加载-D java.ext.dirs选项指定的目录。
- Appclass Loader也称为SystemAppClass 加载当前应用的classpath的所有类,AppClassLoader也是用Java写成的,它的实现类是 sun.misc.Launcher$AppClassLoader。
为什么要有三个类加载器,一方面是分工,各自负责各自的区块,另一方面为了实现委托模型。
Java装载类使用“全盘负责委托机制”。“全盘负责”是指当一个ClassLoder装载一个类时,除非显示的使用另外一个ClassLoder,该类所依赖及引用的类也由这个ClassLoder载入;“委托机制”是指先委托父类装载器寻找目标类,只有在找不到的情况下才从自己的类路径中查找并装载目标类。这一点是从安全方面考虑的,确保系统类只能被Bootstrap ClassLoader加载
类加载器加载顺序
- Bootstrap CLassloder
- Extention ClassLoader
- AppClassLoader
每个加载器都有一个父加载器,可以通过getParent()获得,最上层获得Bootstrap CLassloder 为null,因为其是用C++编写的,在Launch中初始化的时候传的就是null
类加载器流程
- 执行findLoadedClass(String)去检测这个class是不是已经加载过了。
- 执行父加载器的loadClass方法。如果父加载器为null,则jvm内置的加载器去替代,也就是Bootstrap ClassLoader。这也解释了ExtClassLoader的parent为null,但仍然说Bootstrap ClassLoader是它的父加载器。
- 如果向上委托父加载器没有加载成功,则通过findClass(String)查找。
JVM加载class文件的顺序
1、装载:查找和导入Class文件
2、链接:其中解析步骤是可以选择的
(a)检查:检查载入的class文件数据的正确性
(b)准备:给类的静态变量分配存储空间
(c)解析:将符号引用转成直接引用
3、初始化:对静态变量,静态代码块执行初始化工作
定义自已的ClassLoader
定义自已的类加载器分为两步:
1、继承java.lang.ClassLoader
2、重写父类的findClass方法
3.在findClass()方法中调用defineClass(),直接return出去
读者可能在这里有疑问,父类有那么多方法,为什么偏偏只重写findClass方法?
因为JDK已经在loadClass方法中帮我们实现了ClassLoader搜索类的算法,当在loadClass方法中搜索不到类时,loadClass方法就会调用findClass方法来搜索类,所以我们只需重写该方法即可。如没有特殊的要求,一般不建议重写loadClass搜索类的算法。
自定义ClassLoader能做什么
将Class文件按照某种加密手段进行加密,然后按照规则编写自定义的ClassLoader进行解密,这样我们就可以在程序中加载特定了类,并且这个类只能被我们自定义的加载器进行加载,提高了程序的安全性,操作生成的class文件,然后加密,在加载的时候是用特定的加载器进行解密,得到的byte[] 传给defineClass方法,返回Class
Context ClassLoader
类 java.lang.Thread中的方法 getContextClassLoader()和 setContextClassLoader(ClassLoader cl)用来获取和设置线程的上下文类加载器。如果没有通过 setContextClassLoader(ClassLoader cl)方法进行设置的话,线程将继承其父线程的上下文类加载器即AppClassLoader
Java 提供了很多服务提供者接口,这些接口是 Java 核心库的一部分,是由引导类加载器来加载的,引导类加载器是无法找到 SPI 的实现类的,因为它只加载 Java 的核心库。它也不能代理给系统类加载器,因为它是系统类加载器的祖先类加载器。也就是说,类加载器的代理模式无法解决这个问题,Java 应用的线程的上下文类加载器默认就是系统上下文类加载器