类加载器

来源:互联网 发布:淘宝店铺横幅在线制作 编辑:程序博客网 时间:2024/06/07 07:28

 

类加载器的关系:

BootStrap     JRE/lib/rt.jar     //原始加载器
ExtClassLoader  JRE/Lib/Ext/*.jar //扩展加载器
AppClassLoader   $CLASSPATH$*jar    //普通加载器

类加载器采用具有父子关系的树形结构进行组织,在实例化每个类加载器对象时,需要为其指定一个父级类加载器对象或默认采用系统类加载器为其父类。

 

类加载器的获取:

可以通过一个类的Class实例的方法getClassLoader方法获取该类的加载器。

 

类加载器的建立:

ClassLoader抽象类用来描述一个类加载器,继承自ClassLoader的类可实现自定义类加载过程中的功能。

 

类加载器的执行顺序:

每一个类在加载时首先由当前线程所属类加载器获取到要加载类的信息,然后委托给当前线程的父类在相对应的类文件目录下试图加载该类,如果所有父级类都没有找到该类,那么发起者类加载器,也就是当前线程类加载器再试图从当前线程类加载器对应的类文件目录下查找该类,如果仍然未找到需要加载的类文件而抛出ClassNoFoundException异常

换句话说需要一个自定义类加载器加载一个类时,应该保证该类在其父级类加载器对应的目录下没有相同的类文件,否则会被先行加载。

可以通过Thread线程类的
public ClassLoader getContextClassLoader()
public void setContextClassLoader(ClassLoader cl)
来获取和设置当前线程的类加载器

  在多大数的时候Jvm提供的类加载器已经可以完成我们的工作,但是某些特殊需求下还是需要编写自己的类加载器,去完成一些特殊需求。

 

  假如想保证一份Class文件的安全性,最好的办法是将其加密,因为Java的Class文件很容易会反编译,不同于exe之类的可执行程序,毕竟jvm不可能将2进制文件转换成Class文件,再根据操作系统的不同转回2进制文件。这或许也就是Java跨平台的代价吧。而解密的过程我们只能依靠类加载器来完成这一工作。


 

public class PracticeClassLoader {public static void main(String[] args) throws Exception {File fin = new File("C:\\Users\\Administrator\\桌面\\1tack.txt");File fout = new File("C:\\Users\\Administrator\\桌面\\2tack.txt");InputStream in = new BufferedInputStream(new FileInputStream(fin));OutputStream out = new PrintStream(fout);MyClassLoader.code(in, out);}private static void classLoaderTest() throws ClassNotFoundException {ClassLoader cl = null;cl = PracticeClassLoader.class.getClassLoader();System.out.println("普通Path路径加载器: \t" + cl.getClass().getName());System.out.println("Path路径父类加载器: \t" + cl.getParent().getClass().getName());cl = Thread.currentThread().getContextClassLoader();System.out.println("当前线程加载器: \t" + cl.getClass().getName());//设置当前线程类加载器为其原加载器的父类Thread.currentThread().setContextClassLoader(cl.getParent());cl = Thread.currentThread().getContextClassLoader();System.out.println("设置后的加载器: \t" + cl.getClass().getName());cl.loadClass("com.itheima.PracticeClassLoader");}} 
 
  文件加载器
class FileLoader extends ClassLoader {private String rootDir;public FileLoader(String rootDir) {this.rootDir = rootDir;}protected Class findClass(String name) throws ClassNotFoundException {byte[] classData = getClassData(name);if (classData == null) {throw new ClassNotFoundException();}else {return defineClass(name, classData, 0, classData.length);}}private byte[] getClassData(String className) {String path = classNameToPath(className);try {InputStream ins = new FileInputStream(path);ByteArrayOutputStream baos = new ByteArrayOutputStream();int bufferSize = 4096;byte[] buffer = new byte[bufferSize];int bytesNumRead = 0;while ((bytesNumRead = ins.read(buffer)) != -1) {baos.write(buffer, 0, bytesNumRead);}return baos.toByteArray();} catch (IOException e) {e.printStackTrace();}return null;}private String classNameToPath(String className) {return rootDir + File.separatorChar + className.replace('.', File.separatorChar) + ".class";}}

  自行编写类加载器覆写 findClass即可。ClassLoader 类 loadClass 封装了代理模式。首先调用 findLoadedClass检查该类是否已经被加载过;如果没有加载过的话,会调用父类加载器的 loadClass() 方法来尝试加载该类;如果父类加载器无法加载该类的话,就调用 findClass() 方法来查找该类。因此,为了保证类加载器都正确实现代理模式,在开发自己的类加载器时,最好不要覆写 loadClass() 方法,而是覆写 findClass() 方法。

 

  FileLoader 的 findClass根据类的全名在硬盘上查找类的字节代码文件(.class 文件),然后读取该文件内容,最后通过 defineClass() 方法来把这些字节代码转换成Class 类的实例。


原创粉丝点击