天天看点

ClassLoader 深入解析学习笔记(四)

java提供的加载器根加载器,扩展加载器,系统加载器都只能加载指定位置的class和jar,如果我们想要加载其他位置,就比如D盘下某个文件加下的class文件就需要自定义ClassLoader。

自定义ClassLoader最主要的就是要重写findClass方法。

具体代码如下:

package main;

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


public class DefineClassLoader extends ClassLoader {
private String rootDir;//加载文件根路径
    public DefineClassLoader(String rootDir){
        this.rootDir=rootDir;   
    }
    @Override
     public  Class<?> findClass(String className) throws ClassNotFoundException {
        Class clazz=null;
        byte[]classData=getClassData(className);//根据类名获取类的二进制字节数组
        if(classData==null){//获取不到二进制数组,抛出异常
            throw new ClassCastException();
        }
        clazz=defineClass(className,classData, , classData.length);//根据class文件的字节数组转换为Class类实例
        return clazz;
        }
    public byte[]getClassData(String className){
        InputStream is=null;
        ByteArrayOutputStream baos =null;
        String path=classNameToPath(className);
        try{
             is=new FileInputStream(path);
             baos = new ByteArrayOutputStream();  
             byte [] buffer=new byte[*];
             int  bytesNumRead =-;
             while((bytesNumRead=is.read(buffer))!=-){
                 baos.write(buffer, , bytesNumRead);
             }
        return baos.toByteArray();
          }catch (Exception e) {
              e.printStackTrace();
          }finally {
               if(baos!=null){
                   try {
                       is.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
            }
               if(baos!=null){
                   try {
                     baos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
            }
        }
    return null;
    }
    //根据类名获取路径
    public  String classNameToPath(String className){
        return rootDir+className.replace(".", File.separator)+".class";
    }
}
           

我们在d盘下创建如下目录,并新建DefineClass类

ClassLoader 深入解析学习笔记(四)
package com.test;

public class DefineClass {
    public String toString () {
        return "加载自定义类:DefineClass!";
    }
}
           

并用dos命令进行编译,注意类里面有中文,还要加上编码进行编译,命令为:

编译后再创建测试类DefineClassLoaderTest ,代码如下

package test;



import org.junit.Test;

import main.DefineClassLoader;

public class DefineClassLoaderTest {
    @Test
    public void test() throws ClassNotFoundException, InstantiationException, IllegalAccessException{
        String rootdir="D:\\";
        String className="com.test.DefineClass";
        DefineClassLoader classloader=new DefineClassLoader(className);
        Class clazz=classloader.loadClass(className);
        System.out.println(className+"的加载器是"+clazz.getClassLoader());
        System.out.println(clazz.newInstance().toString());
    }

}
           

执行结果

ClassLoader 深入解析学习笔记(四)

可见DefineClass类的加载器正是我们自定义的加载器。

这里再次为了证明双亲委托加载模式,我们在工程下也创建一个与D盘下面一样的com.test.DefineClass类,那程序的输出的加载器肯定是App加载器。

ClassLoader 深入解析学习笔记(四)

程序执行结果与预期一样:

ClassLoader 深入解析学习笔记(四)