【java】类加载器和委托机制

(一)类加载的两种方式
1. 隐式加载:通过new等方式生成对象时,隐式调用类装载器加载对应的类到JVM中,也就是自动加载。
Student stu =new Student();
2. 显式加载:当我们调用Class.fromName()等方法时显式加载对应的类到JVM中。
Class stu =Class.fromName("包名.Student");
(二)类加载的动态性
一个应用程序往往有好很多个类,实际并不会一下子把全部类加载到JVM,而是将程序能正常运行的基础类加载到JVM,等其他类需要用到时再加载到JVM,减少了内存的开销。
(三)类装载器种类
1. Bootstrp loader

Bootstrp加载器是用C++语言写的,它是在Java虚拟机启动后初始化的,它主要负责加载%JAVA_HOME%/jre/lib,-Xbootclasspath参数指定的路径以及%JAVA_HOME%/jre/classes中的类。

2.ExtClassLoader

Bootstrp loader加载ExtClassLoader,并且将ExtClassLoader的父加载器设置为Bootstrp loader.ExtClassLoader是用Java写的,具体来说就是 sun.misc.Launcher$ExtClassLoader,ExtClassLoader主要加载%JAVA_HOME%/jre/lib/ext,此路径下的所有classes目录以及java.ext.dirs系统变量指定的路径中类库。

3.AppClassLoader

Bootstrp loader加载完ExtClassLoader后,就会加载AppClassLoader,并且将AppClassLoader的父加载器指定为 ExtClassLoader。AppClassLoader也是用Java写成的,它的实现类是 sun.misc.Launcher$AppClassLoader,另外我们知道ClassLoader中有个getSystemClassLoader方法,此方法返回的正是AppclassLoader.AppClassLoader主要负责加载classpath所指定的位置的类或者是jar文档,它也是Java程序默认的类加载器。

我们可以将三个类加载器用下图表示:

图片无法显示

三个类加载器会在JVM运行时产生,它们各司其职,协调工作,同时为了实现双亲委托模型打下基础。
(四)双亲委托机制
当我们需要加载一个类时,是如何判断由哪个加载器负责加载呢?Java里用 双亲委托模型 来解决这个问题的,即"当需要加载类到JVM时,会先请示其父亲加载器使用其搜索路径帮忙载入,如果父亲加载器找不到,那么才由自己依照自己的搜索路径搜索类"。
举个栗子:
Public  class  Test{
Public  static  void  main(String[]  arg){
ClassLoader  c=  Test.class.getClassLoader();//获取Test类的类加载器
System.out.println(c);
ClassLoader c1  =  c.getParent();//获取c这个类加载器的父类加载器
System.out.println(c1);
ClassLoader c2  =  c1.getParent();//获取c1这个类加载器的父类加载器
System.out.println(c2);
     }
}
运行结果:
AppClassLoader
ExtClassLoader
Null
可以看出Test是由AppClassLoader加载器加载的,AppClassLoader的父亲加载器是 ExtClassLoader,但是ExtClassLoader的父亲为 null 是怎么回事呵,朋友们留意的话,前面有提到Bootstrap Loader是用C++语言写的,依java的观点来看,逻辑上并不存在Bootstrap Loader的类实体,所以在java程序代码里试图打印出其内容时,我们就会看到输出为null。
(五)全盘负责委托机制
全盘负责是指当一个ClassLoder装载一个类时,除非显示的使用另外一个ClassLoder,该类所依赖及引用的类也由这个ClassLoder载入。

举个栗子:

class Test{
public static void main(String[] args){
Scanner sc =new Scanner(System.in);
}
从上面我们可以知道Test的引用类是Scanner,那么加载器在加载Test类时,Scanner也会由同一个加载器进行装载。
(六)类加载器的实现过程
类加载器就是在JVM中负责解析Class字节码文件,并将解析得到的结果打包构造成Class对象,其具体流程如下:

1.装载: 查找和导入Class文件
2.链接:(a)检查:检查载入的class文件数据的正确性
      (b)准备:给类的静态变量分配存储空间
         ©解析:将符号引用转成直接引用
3.初始化: 对静态变量,静态代码块执行初始化工作

(七)双亲委托机制的好处
1.节省时间复杂度

避免重复加载,当父亲已经加载了该类的时候,就没有必要子ClassLoader再加载一次。

2.保证安全

例如我们渴望写一个java.lang.String类来代替JDK里本身的类,但是我们知道由于双亲模型机制的存在,最后加载的肯定是JDK自带的String类,那么我们想能不能自定义一个ClassLoader来加载自己写的java.lang.String类呢?其实即使这样也是不可能成功的,因为JVM里已经决定了针对java.*开头的类必须由Bootstrp loader装载。

(八)自定义ClassLoader
遵循绝不重复造轮子的原则,我们需要问一问为什么JVM已经有自带的默认类加载器,我们还要自己去定义,不是多此一举吗?
其实是因为Java中提供的默认ClassLoader,只加载指定目录下的jar和class,如果我们想加载其它位置的类或jar时,比如:我要加载网络上的一个class文件,通过动态加载到内存之后,要调用这个类中的方法实现我的业务逻辑。在这样的情况下,默认的ClassLoader就不能满足我们的需求了,所以需要定义自己的ClassLoader。
定义自已的类加载器分为两步:

1、继承java.lang.ClassLoader
2、重写父类的findClass方法

父类有那么多方法,为什么偏偏只重写findClass方法?
因为JDK已经在loadClass方法中帮我们实现了加载器搜索类时的算法,当在loadClass方法中搜索不到类时,loadClass方法就会调用findClass方法来搜索类,所以我们只需重写该方法即可。
发布了19 篇原创文章 · 获赞 51 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_42370146/article/details/96857836