自定义类加载器及其双亲委托机制

JAVA自带的类加载器

JAVA的类加载器分为三种,启动类加载器(系统类加载器)、扩展类加载器以及系统类加载器
由前到后每一个都是下一个的父加载器
获取类加载器以及加载器的加载路径

		System.out.println("----------------三种类加载器-------------------");
        System.out.println(Launcher.getLauncher().getClassLoader());
        // 启动类加载器输出为空,别慌不是你错了,这就是正确结果
        System.out.println(Launcher.getLauncher().getClassLoader().getParent());
        System.out.println(Launcher.getLauncher().getClassLoader().getParent().getParent());
        System.out.println("---------------------各个类加载的路径---------------------------");
        System.out.println(System.getProperty("sun.boot.class.path"));
        System.out.println(System.getProperty("java.ext.dirs"));
        System.out.println(System.getProperty("java.class.path"));

输出结果

----------------三种类加载器-------------------
sun.misc.Launcher$AppClassLoader@14dad5dc
sun.misc.Launcher$ExtClassLoader@74a14482
null
---------------------各个类加载的路径---------------------------
C:\Java\jdk1.8.0_65\jre\lib\resources.jar;C:\Java\jdk1.8.0_65\jre\lib\rt.jar;C:\Java\jdk1.8.0_65\jre\lib\sunrsasign.jar;C:\Java\jdk1.8.0_65\jre\lib\jsse.jar;C:\Java\jdk1.8.0_65\jre\lib\jce.jar;C:\Java\jdk1.8.0_65\jre\lib\charsets.jar;C:\Java\jdk1.8.0_65\jre\lib\jfr.jar;C:\Java\jdk1.8.0_65\jre\classes
C:\Java\jdk1.8.0_65\jre\lib\ext;C:\WINDOWS\Sun\Java\lib\ext
C:\Java\jdk1.8.0_65\jre\lib\charsets.jar;C:\Java\jdk1.8.0_65\jre\lib\deploy.jar;C:\Java\jdk1.8.0_65\jre\lib\ext\access-bridge-64.jar;C:\Java\jdk1.8.0_65\jre\lib\ext\cldrdata.jar;C:\Java\jdk1.8.0_65\jre\lib\ext\dnsns.jar;C:\Java\jdk1.8.0_65\jre\lib\ext\jaccess.jar;C:\Java\jdk1.8.0_65\jre\lib\ext\jfxrt.jar;C:\Java\jdk1.8.0_65\jre\lib\ext\localedata.jar;C:\Java\jdk1.8.0_65\jre\lib\ext\nashorn.jar;C:\Java\jdk1.8.0_65\jre\lib\ext\sunec.jar;C:\Java\jdk1.8.0_65\jre\lib\ext\sunjce_provider.jar;C:\Java\jdk1.8.0_65\jre\lib\ext\sunmscapi.jar;C:\Java\jdk1.8.0_65\jre\lib\ext\sunpkcs11.jar;C:\Java\jdk1.8.0_65\jre\lib\ext\zipfs.jar;C:\Java\jdk1.8.0_65\jre\lib\javaws.jar;C:\Java\jdk1.8.0_65\jre\lib\jce.jar;C:\Java\jdk1.8.0_65\jre\lib\jfr.jar;C:\Java\jdk1.8.0_65\jre\lib\jfxswt.jar;C:\Java\jdk1.8.0_65\jre\lib\jsse.jar;C:\Java\jdk1.8.0_65\jre\lib\management-agent.jar;C:\Java\jdk1.8.0_65\jre\lib\plugin.jar;C:\Java\jdk1.8.0_65\jre\lib\resources.jar;C:\Java\jdk1.8.0_65\jre\lib\rt.jar;E:\亿达信息\GoodGoodStadyDayDayUp\out\production\GoodGoodStadyDayDayUp;E:\亿达信息\GoodGoodStadyDayDayUp\lib\mysql-connector-java-5.0.8-bin.jar;E:\idea\IntelliJ IDEA 2017.2\lib\idea_rt.jar

在这些路径中(每个人的也许会不同,所以按照自己的来修改),可以尝试把自己写的Class放入到指定的文件中(ExtClassLoader只加载Jar包中的类),去通过getClassLoader()方法获取已验证结果,这里不做过多介绍。
如果你很好奇我怎么知道获取路径的是这些参数,那就请参考一下sun.misc.Launcher源代码。。。

自定义类加载器

代码示例

package com.programer.wenbin.jvm;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;

public class MyClassLoader extends ClassLoader {

    private String path;
    private String classLoaderName;
    private final String fileExtension = ".class";

    public MyClassLoader(String classLoaderName) {
        super();// 使用系统类加载器作为父加载器
        this.classLoaderName = classLoaderName;
    }

    public MyClassLoader(ClassLoader parent, String classLoaderName) {
        super(parent);// 使用传入的类加载器作为父加载器
        this.classLoaderName = classLoaderName;
    }

    /**
     * 根据文件的地址,以字节的形式加载class文件。
     *
     * @param {String}name
     * @return 加载class的byte数组
     */
    private byte[] loadClassData(String name) {
        InputStream is = null;
        byte[] data = null;
        ByteArrayOutputStream baos = null;
        try {
            name = name.replace(".", "\\");
            is = new FileInputStream(new File(this.path + name + this.fileExtension));
            baos = new ByteArrayOutputStream();
            int ch = 0;
            while (-1 != (ch = is.read())) {
                baos.write(ch);
            }
            data = baos.toByteArray();
        } catch (Exception ex) {
            ex.printStackTrace();
        } finally {
            try {
                is.close();
                baos.close();
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
        return data;
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        System.out.println("----------------------This is MyClassLoader------------------");
        byte[] data = this.loadClassData(name);
        return super.defineClass(name, data, 0, data.length);
    }

    public void setPath(String path) {
        this.path = path;
    }
}


打开ClassLoader类会看到注释中解释了怎么建立了一个简单的自定义加载器
测试加载器

 		MyClassLoader loader = new MyClassLoader("loader1");
        loader.setPath("C:\\Users\\13559\\Desktop\\jvmtest\\");
        // com.programer.wenbin.jvm.TestUse这个类是自己建立的一个普通java类,可以没有任何一个属性和方法
        Class<?> clazz = loader.loadClass("com.programer.wenbin.jvm.TestUse");
        System.out.println(clazz.getClassLoader());

setPath是这只一个加载的路径我们把需要加载的类放入进去即可,如下图
在这里插入图片描述
测试已经准备好,准备你们的小手点击运行,那么问题来了!
sun.misc.Launcher$AppClassLoader@14dad5dc
这输出的怎么是AppClassLoader(系统类加载器),这也就是今天要说的重点双亲委托机制

双亲委托机制

  • 概念
    当使用一个类加载器去加载一个class对象的时候,这个加载器会用自己的父类加载器去尝试加载,如果加载不成功才会自己去加载
    在这里插入图片描述
    会发现递归调用,这也就是双亲委托的核心代码
  • 啃老的好处
    这里有一个命名空间的概念,我以后可能会专门发一个关于这个的,如果没有(懒了),请大家去CSDN中进行搜索。这里简要的说一下:
    命名空间是由加载器以及所有的父类加载器所组成的,自加载器可以访问父类加载的类,父类加载器不能访问子加载器加载的类,儿子可以用爸爸的,爸爸不可以用儿子的,兄弟之间的也是不可以用的,由此可以得出结论加载器的一家过的很不和谐
    …有点说多了,简单的说一下好处吧,这样就能保证JAVA的核心类,比如最牛*的Object类,是由启动类加载器加载的,这样使的Object只加载一次,因为如果已经加载过的类,并且是可见的,那么就不会再一次加载,“开箱即用”。如果对于这个类不可见的话,就会再一次加载Obejct,所以存在很多版本的Object类,使得程序存在潜在风险(JDK设计人员的脑子牛!),但是这种也不是万能的,比如JDBC等,所以有的时候会违背双亲委托机制,会使用线程上下文类加载器,以后别的文章会说。

如果想用自己的类加载器加载,怎么办呢?
上面开始提到过AppClassLoader的加载路径,那么我们就把已经编译好的文件删除掉去运行,如果是ecplise就打开文件夹进行删除,idea在目录里进行删除即可

在这里插入图片描述
删除之后请大家再次运行
在这里插入图片描述

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值