自定义类加载器(二)

本文在原有知识基础上对Java自定义类加载器进行优化,包括不重写ClassLoader、批量加载类文件。介绍了新类加载器的创建原理与代码,通过定义被加载类、编译文件、定义测试类进行测试,最后总结了自定义类加载器的大体套路。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在前面的知识基础上对自定义类加载器进行优化

优化内容:

①不对ClassLoader进行任何重写,完全自定义

②批量加载类文件

一.新类加载器的创建

1.实现原理:

①手动指定加载器加载文件的根路径

②手动指定需要被加载的类文件数组

③自定义类加载器命名为CustomClassLoader并继承ClassLoader

④定义私有成员属性

1>String rootdir用于记录根路径

2>Map<String,Class>用于记录所有加载后的类文件对象

⑤定义带参构造函数,构造函数内完成的内容如下:

1>完成成员属性的初始化

2>完成对所有指定的类的加载

2.实现代码如下:

package Myloader02;

import java.io.*;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;

/**
 * @author :weihuanwen
 * @date :Created in 2019/5/23 00:14
 * @description :自定义类加载器(多批次加载)
 * @version: 1.0
 */
public class CustomClassLoader extends ClassLoader {

    //该类加载器直接加载类文件的根路径
    private String rootdir;
    //用于存储加载的类文件对象
    private Map<String,Class> clazzs;

    //提供外界获取接口
    public Map<String, Class> getClazzs() {
        return clazzs;
    }

    /**
     * 类加载器构造函数
     * @param basedir 指定类加载器加载根目录
     * @param classNames 需要被加载的类的文件名数组
     */
    public CustomClassLoader(String basedir,String[] classNames) {
        //此加载器的父加载器被定义为null
        super(null);
        //初始化类加载器加载路径根目录
        this.rootdir = basedir;
        //初始化存储加载后的类文件的集合
        clazzs = new HashMap<>();
        //获取形参列表中所有类的加载文件对象
        getClazzs(classNames);
    }


    /**
     * 将形参列表中指定的类名全部加载为对应的类文件对象
     * @param classNames 类文件名数组
     */
    private void getClazzs(String[] classNames) {
        for (int i = 0; i < classNames.length; i++) {
            //当前的文件名
            String className = classNames[i];
            //获取文件对象
            File classFile = loadClassFile(className);
            try {
                //将类文件加载为字节数组
                byte[] classData = loadClassData(classFile);
                if (classData == null){
                    throw new ClassNotFoundException();
                }else{
                    //将字节数组转换为类文件对象
                    Class clazz = defineClass(className, classData, 0, classData.length);
                    //将加载出的类文件对象存储于map集合中
                    clazzs.put(className,clazz);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 根据文件名获取对应的文件对象
     * @param className
     * @return
     */
    private File loadClassFile(String className){
        //加载目录根路径
        StringBuilder sb = new StringBuilder(rootdir);
        //获取当前类对应的class文件名
        String classFullName = className.replace('.', File.separatorChar) + ".class";
        sb.append(File.separatorChar + classFullName);
        //获取类对应的class文件
        return new File(sb.toString());
    }

    /**
     * 将类文件转换为字节数组
     * @param classFile
     * @return
     */
    private byte[] loadClassData(File classFile){
        try {
            //获取class文件对应的字节流
            InputStream is = new FileInputStream(classFile);
            //该字节流可以将数据写入字节数组,数据写入缓冲区时缓冲区自动增长
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            byte[] bytes = new byte[4096];
            int length = 0;
            //读取类文件并写入字节数组中
            while ((length = is.read(bytes))!= -1){
                baos.write(bytes,0,length);
            }
            //返回类文件的字节数组
            return baos.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

二.测试类加载器:

1.定义被加载类SayHi.java

/**
 * @author :weihuanwen
 * @date :Created in 2019/5/23 00:14
 * @description :
 * @version: 1.0
 */
public class SayHi {

    @Override
    public String toString() {
        return "Hi!";
    }
}

2.定义被加载类SayGoodBye.java

/**
 * @author :weihuanwen
 * @date :Created in 2019/5/23 00:14
 * @description :
 * @version: 1.0
 */
public class SayGoogBye {

    @Override
    public String toString() {
        return "GoogBye!";
    }
}

3.将java文件放置在D:\study下并执行编译,编译后效果如下:

4.定义测试类test.java

package Myloader02;

import java.util.Map;

/**
 * @author :weihuanwen
 * @date :Created in 2019/5/23 00:14
 * @description :
 * @version: 1.0
 */
public class test {

    public static void main(String[] args) throws Exception{
        CustomClassLoader ccl = new CustomClassLoader("D:\\study",
                new String[]{"SayHi","SayGoogBye"});
        Map<String, Class> clazzs = ccl.getClazzs();
        for (String className : clazzs.keySet()) {
            String msg = clazzs.get(className).newInstance().toString();
            System.out.println(msg);
        }
    }
}

5.测试结果输出如下:

Hi!
GoogBye!

三.总结

自定义类加载器时不一定要重写ClassLoader的findClass(String name)方法,自定义的大体套路都是此,先确定加载路径,再确定需要被加载的类文件名,定义字节流读取class文件后转为字节数组.通过字节数组便可以获取该类的class文件对象.通过class文件对象,通过反射就可以操控类的内容(类的方法,类的属性)了.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值