带你玩玩类加载器

275 阅读2分钟

前言

看大部分博客讲到类加载器时都会讲一堆理论,稍显枯燥,今天我就来带大家玩点好玩的,手动加入拓展类,看看是不是真的是由扩展类加载器所加载?

类加载器

  • Bootstrap ClassLoader(启动类加载器)负责将%JAVA_HOME%/lib目录中或-Xbootclasspath中参数指定的路径中的,并且是虚拟机识别的(按名称)类库加载到JVM中
  • Extension ClassLoader(扩展类加载器)负责加载%JAVA_HOME%/lib/ext中的所有类库
  • Application ClassLoader(应用程序加载器) 负责ClassPath中的类库,就是用户定义的类

扩展类加载器

package bingo;
public class Bingo {
    public static void main(String[] args) {
        System.out.println("关注J2彬彬");
    }
}

ext文件夹就是JDK存放扩展类的地方,将上面这段代码编译成class文件,放到下图的的bingo文件夹下

直接将bingo文件夹打成jar包,执行下图命令,打成jar包后如上图所示产生一个bingo.jar
在IDEA的类路径下可以看到我加入的jar包

下面是个简单的案例

package com.bingo.classloader;

import bingo.Bingo;


public class ClassLoaderTest {

    public static void main(String[] args) {

        Object object = new Object();
        System.out.println("object加载器:"+object.getClass().getClassLoader());

        Bingo bingo = new Bingo();
        System.out.println("bingo加载器:"+bingo.getClass().getClassLoader());
        System.out.println("bingo加载器的父类:"+bingo.getClass().getClassLoader().getParent());

        ClassLoaderTest test = new ClassLoaderTest();
        System.out.println("test加载器:"+test.getClass().getClassLoader());
        System.out.println("test加载器的父类:"+test.getClass().getClassLoader().getParent());
        System.out.println("test加载器的父类的父类:"+test.getClass().getClassLoader().getParent().getParent());

    }
}

执行结果:

object加载器:null
bingo加载器:sun.misc.Launcher$ExtClassLoader@29453f44
bingo加载器的父类:null
test加载器:sun.misc.Launcher$AppClassLoader@18b4aac2
test加载器的父类:sun.misc.Launcher$ExtClassLoader@29453f44
test加载器的父类的父类:null

双亲委派模型

由执行结果可以看出,Bingo类由ExtClassLoader加载,用户自定义的类一般都是由AppClassLoader加载,而Object类在rt.jar下,由Bootstrap加载器加载,它是由C++实现的,所以打印结果为null。getParent()得到父类加载器,这里就可以看出双亲委派模型。

为什么要加载类需要委托双亲加载呢?

  • 每一个类都只会被加载一次,避免了重复加载
  • 每一个类都会被尽可能的加载(从引导类加载器往下,每个加载器都可能会根据优先次序尝试加载它)
  • 有效避免了某些恶意类的加载(比如自定义了Java。lang.Object类,一般而言在双亲委派模型下会加载系统的Object类而不是自定义的Object类)

我是广州的java程序员小彬,一直在致力于java后端的学习,下面是我的微信公众号,里面有更多有质量的文章,感谢大家!