类加载机制的学习3___自定义的类加载器

    在程序中实现一个自定的类加载器:继承ClassLoader抽象类,重写findClass()。 如下位一个实例代码:

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.WritableByteChannel;

public class MyClassLoaderTest extends ClassLoader{

    private String filePath; //文件路径
    private String className; //类名

    public MyClassLoaderTest(String filePath,String className){
        this.filePath=filePath;
        this.className=className;
    }

    //获得被加载的类名
    public String getClassName(){
        return className;
    }

    @Override
    protected Class<?> findClass(String s) throws ClassNotFoundException {

        File file = new File(filePath+className+".class");

        try {
            //将字节码文件转化成字节数组
            byte[] classBytes = getClassBytes(file);

            //采用defineClass()方法将字节数组转化成class对象实例
            Class<?> aClass = defineClass(s, classBytes, 0, classBytes.length);
            return aClass;
        } catch (IOException e) {
            e.printStackTrace();
        }

        //交给超类加载器进行加载
        return super.findClass(s);
    }

    //将字节码文件转化成字节数组
    public byte[] getClassBytes(File file) throws IOException {
        //文件输入流  读取文件
        FileInputStream fileInputStream = new FileInputStream(file);

        //文件通道 read(byteBuffer)  获得缓冲区中的数据
        FileChannel channel = fileInputStream.getChannel();

        //字节数组 toByteArray()  将输出流中的数据转换成一个字节数组
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

        //可写的字节管道
        WritableByteChannel writableByteChannel = Channels.newChannel(byteArrayOutputStream);

        //申请一个字节缓存大小
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);

        //具体操作
        while(true){
            try {
                int i = channel.read(byteBuffer); //从该通道获取到给定缓冲区的字节数列
                if(i==0 || i==-1){
                    break;
                }
                byteBuffer.flip();
                writableByteChannel.write(byteBuffer);
                byteBuffer.clear();


            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        fileInputStream.close();
        return byteArrayOutputStream.toByteArray();//将输出数据转换成字节数组
    }

    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {

        MyClassLoaderTest loaderTest = new MyClassLoaderTest("D:/","People");

        //使用给定的类加载器对目标类进行加载  Class.forName(...) 进行手动加载
        Class<?> peopleClass = Class.forName(loaderTest.getClassName(), true, loaderTest);

        //通过反射从class对象来获得类的对象实例
        Object p = peopleClass.newInstance();

        System.out.println(p);

        //通过class对象实例,来获得具体的类加载器
        System.out.println(p.getClass().getClassLoader());

    }
}

输出结果:

I am a people, my name is null
MyClassLoaderTest@1540e19d

 

总结:

1.为什么要自定义类的加载器???

通过自定义的类加载器,我们可以进行一些额外的操作,比如可以对字节码文件进行加密,解密。这就保护了原始字节码的安全性问题。

2.为什么要使用双亲委派机制,进行类的加载???

采用双亲委派机制,保证了类加载的全局唯一性,比如,java.lang.String 类是由启动类加载器(Bootstrap ClassLoader)进行加载的,当用户自定义一个String时,若不采用双亲委派机制,AppClassLoader就也会对用户自定义的String类进行加载,这就会使得Java体系类的原生类遭到破坏。

 

posted @ 2019-08-26 15:12  德鲁大叔817  阅读(251)  评论(0编辑  收藏  举报