java类加载器

本文详细介绍了Java类加载器的分类,包括BootstrapClassLoader、ExtensionClassLoader、ApplicationClassLoader和UserClassLoader。讨论了双亲委派机制,防止不同加载器加载同一类导致混乱,并通过示例展示了如何通过自定义类加载器破坏这一机制。此外,还提到了JDBC加载第三方库和热部署如何打破双亲委派规则。
摘要由CSDN通过智能技术生成

晚上没啥事,想到白天写的类加载过程,现在就忍不住把类加载器也说一下,其实在刚开始之前我是对类加载器没什么概念的,编程最初我只知道自己编写的代码能够输出自己想要的结果,而对于这个结果究竟是怎么产生的,我并不是关心,但是随着工作时间久了,就会发现熟悉类加载过程也是会有一定帮助的。

那么类加载是用来干什么的呢?我们需要类加载器的场景又是怎么样的呢? 接下来我们就一起来看看类加载器。

首先,我们先来介绍一下类加载的种类,如下图:

图来自于网络:

在这里插入图片描述

Java的类加载器一般来说分为两大类,一类是系统提供的,一类是开发人员自己编写的

系统提供的:

Bootstrap ClassLoader: 底层是用c/c++编写的,无法作为对象被程序引用,主要用来加载java下面lib文件的class文件

Extension ClassLoader: 主要用来加载java的扩展类,java的lib文件下面的ext

Application ClassLoader:我们自己编写的class文件就是通过这个来加载的

User ClassLoader:用户自己编写的类加载器,用户可以获取任意来源的class文件

那么小伙伴们肯定就会有疑问,这么类加载器,要是都去加载同一个class文件,那么会不会出现混乱呢?

其实是会的,因为每个类加载器都有属于自己的命名空间,当不同的类加载器去加载同一个class文件时,所生成的对象是不相同的,Java为了避免这个现象,就出现双亲委派这一概念。具体怎么样的呢,如下图:

图来源于网络:
在这里插入图片描述

当我们的程序需要去加载一个class文件的时候,都是先主动去找它的父加载器,如果父加载器有了就直接用父加载器加载,如果父加载器没有找到对应的class文件,才会用下面的加载器去加载,这样就能确保一个class文件只会被一个加载器去加载,不会产生歧义。

在正常情况下,如果我们正常的使用类加载器是没有什么问题的,但是这样双亲委派的机制,在一定的情况下也是可以被破坏的

情况一:自定义类加载器并且重写loadClass方法。这样对于同一个class文件就会有不同的类实例出现,造成混乱。

情况二:通过Bootstrap ClassLoader来调用第三方的class文件,这个是由上而下的,和刚刚所说的由下而上正好相反,举个例子来说,比如jdbc的调用(java spi)

情况三:热部署

来用实际的案例来掩饰一下情况一:

我们通过自定义类加载器来复现这一种情况,那么怎么自定义类加载器呢,其实很简单,只需要继承ClassLoad类,然后重写loadClass方法就可以的,通过代码来演示一下:

public class ClassLoadTest {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        ClassLoader myClassLoader = new ClassLoader() {
            @SneakyThrows
            @Override
            public Class<?> loadClass(String name) throws ClassNotFoundException {
                //读取本地的class文件
                String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class";
                InputStream resourceAsStream = getClass().getResourceAsStream(fileName);
                if (resourceAsStream == null) {
                    return super.loadClass(name);
                }
                byte[] b = new byte[resourceAsStream.available()];
                resourceAsStream.read(b);
                return defineClass(name, b, 0, b.length);
            }
        };
        Class<?> loadClass = myClassLoader.loadClass("com.znv.demo_service.log.LogTest");
        Object o = loadClass.newInstance();
        System.out.println(o instanceof com.znv.demo_service.log.LogTest);

    }
}

上述代码输出的结果是false,这也就验证了上面所说的,不用的加载器有个不同的命名空间,所以上面自定义的加载器和java系统的加载器同时加载同一个类的时候其实例化的对象不是同的。所以可以通过自定义类加载器的方式来破坏双亲委派的机制。

下面我们通过JDBC的案例来说说案例二:

我们知道数据库的厂家有很多种,那么对应的连接方式也有很多,Java的jdk不可能把所有连接方式都写入,所以java就给所有的数据库厂商定义了一个接入规范,然后厂商就按照这个规范去开发连接工具包,在java中需要的时候,就通过类加载器去加载这些第三方的工具包,那么问题来了,我们知道类加载器加载class文件是自上而下的,现在变成了有java去主动加载第三方工具包,这也就破环了双亲委派的机制。

情况三就是热部署,在我们的项目启动的时候,我们想动态的去替换一些class文件,这里也是需要java类加载器去主动加载这些class文件的。

这篇文章主要是介绍了java类加载器的分类,双亲委派的机制,为什么需要双亲委派,怎么去自定义类加载器,还有双亲委派被打破的场景。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值