类加载机制的学习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体系类的原生类遭到破坏。