ClassLoader

来源:互联网 发布:遗传算法原理及应用 编辑:程序博客网 时间:2024/06/05 15:08

目录 1、类加载器概述 2、类加载器的委托机制 3、自定义类加载器

1、 类加载器概述:

1、定义:简单说,类加载器就是加载类的工具。

当出现一个类,用到此类的时候,Java虚拟机首先将类字节码加载进内存,通常字节码的原始信息放在硬盘上的classpath指定的目录下。

2、类加载器作用:将.class文件中的内容加载进内存进行处理,处理完后的结果就是字节码。

3、默认类加载器:

1Java虚拟机中可安装多个类加载器,系统默认的有三个主要的,每个类负责加载特定位置的类:BootStrapExtClassLoaderAppClassLoader

2BootStrap--顶级类加载器:

类加载器本身也是Java类,因为它是Java类,本身也需要加载器加载,显然必须有第一个类加载器而不是java类的,这正是BootStrap。它是嵌套在Java虚拟机内核中的,已启动即出现在虚拟机中,是用c++写的一段二进制代码。所以不能通过java程序获取其名字,获得的只能是null

4Java虚拟机中的所有类加载器采用子父关系的树形结构进行组织,在实例化每个类加载器对象或默认采用系统类加载器作为其父级类加载器。

示意图:

示例:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. package cn.itcast.text2;    
  2. import java.util.Date;    
  3. public class ClassLoadTest{    
  4.     public static void main(String[] args) throws Exception{    
  5.         System.out.println(    
  6.                 ClassLoadTest.class.getClassLoader().    
  7.                 getClass().getName());//为AppClassLoader     
  8.         System.out.println(    
  9.                 System.class.getClassLoader());//为null     
  10.     }    
  11. }    


 

2、类加载器的委托机制:

1、加载类的方式

Java虚拟机要加载一个类时,到底要用哪个类加载器加载呢?

1)首先,当前线程的类加载器去加载线程中的第一个类。

2)若A引用类B(继承或者使用了B),Java虚拟机将使用加载类的类加载器来加载类B

3)还可直接调用ClassLoaderLoaderClass()方法,来制定某个类加载器去加载某个类。

2、加载器的委托机制:每个类加载器加载类时,又先委托给上级类加载器。

每个ClassLoader本身只能分别加载特定位置和目录中的类,但他们可以委托其他类的加载器去加载,这就是类加载器的委托模式,类加载器一级级委托到BootStrap类加载器,当BootStrap在指定目录中没有找到要加载的类时,无法加载当前所要加载的类,就会一级级返回子孙类加载器,进行真正的加载,每级都会先到自己相应指定的目录中去找,有没有当前的类;直到退回到最初的类装载器的发起者时,如果它自身还未找到,未完成类的加载,那就报告ClassNoFoundException的异常。

简单说,就是先由发起者将类一级级委托为BootStrap,从父级开始找,找到了直接返回,没找到再返回给其子级找,直到发起者,再没找到就报异常。

3、委托机制的优点:可以集中管理,不会产生多字节码重复的现象。

补充:面试题

可不可以自己写个类为:java.lang.System呢?

回答:第一、通常是不可以的,由于类加载器的委托机制,会先将System这个类一级级委托给最顶级的BootStrap,由于BootStrap在其指定的目录中加载的是rt.jar中的类,且其中有System这个类,那么就会直接加载自己目录中的,也就是Java已经定义好的System这个类,而不会加载自定义的这个System

第二、但是还是有办法加载这个自定义的System类的,此时就不能交给上级加载了,需要用自定义的类加载器加载,这就需要有特殊的写法才能去加载这个自定义的System类的。

3、自定义类加载器

1、自定义的类加载器必须继承抽象类ClassLoader,要覆写其中的findClass(String name)方法,而不用覆写loadClass()方法。

2、覆写findClass(String name)方法的原因:

1)是要保留loadClass()方法中的流程,因为loadClass()中调用了findClass(String name)这个方法,此方法返回的就是去寻找父级的类加载器。

2)在loadClass()内部是会先委托给父级,当父级找到后就会调用findClass(String name)方法,而找不到时就会用子级的类加载器,再找不到就报异常了,所以只需要覆写findClass方法,那么就具有了实现用自定义的类加载器加载类的目的。

流程:

父级-->loadClass-->findClass-->得到Class文件后转化成字节码-->defind()

3、编程步骤:

1)编写一个对文件内容进行简单加盟的程序

2)编写好了一个自己的类加载器,可实现对加密过来的类进行装载和解密。

3)编写一个程序,调用类加载器加载类,在源程序中不能用该类名定义引用变量,因为编译器无法识别这个类,程序中除了可使用ClassLoaderload方法外,还能使用放置线程的上线文类加载器加载或系统类加载器,然后在使用forName得到字节码文件。

示例:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. //加密文件     
  2. package cn.itcast.text2;    
  3. import java.util.Date;    
  4.     
  5. public class ClassLoaderAttachment extends Date {    
  6.     //对此类进行加密     
  7.         public String toString(){    
  8.             return "hello world";    
  9.         }    
  10.         public static void main(String [] args){    
  11.                 
  12.         }    
  13. }    
  14.     
  15. //自定义类加载器     
  16. package cn.itcast.text2;    
  17.     
  18. import java.io.*;    
  19. //继承抽象类ClassLoader     
  20. public class MyClassLoader  extends ClassLoader {    
  21.     public static void main(String[] args) throws Exception {    
  22.         //传入两个参数,源和目标     
  23.         String scrPath = args[0];    
  24.         String destDir = args[1];    
  25.         //将数据读取到输入流中,并写入到输出流中     
  26.         FileInputStream fis = new FileInputStream(scrPath);    
  27.         String destFileName =     
  28.                 scrPath.substring(scrPath.lastIndexOf('\\')+1);    
  29.         String destPath = destDir + "\\" + destFileName;    
  30.         FileOutputStream fos = new FileOutputStream(destPath);    
  31.         //加密数据     
  32.         cypher(fis,fos);    
  33.         fis.close();    
  34.         fos.close();    
  35.     }    
  36.     //定义加密数据的方法     
  37.     private static void cypher(InputStream ips,OutputStream ops)throws Exception{    
  38.         int b = 0;    
  39.         while((b=ips.read())!=-1){    
  40.             ops.write(b ^ 0xff);    
  41.         }    
  42.     }    
  43.     //定义全局变量     
  44.     private String classDir;    
  45.     @Override//覆写findClass方法,自定义类加载器     
  46.     protected Class<?> findClass(String name) throws ClassNotFoundException {    
  47.         String classFileName = classDir + "\\" + name + ".class";     
  48.         try {    
  49.             //将要加载的文件读取到流中,并写入字节流中     
  50.             FileInputStream fis = new FileInputStream(classFileName);    
  51.             ByteArrayOutputStream bos = new ByteArrayOutputStream();    
  52.             cypher(fis,bos);    
  53.             fis.close();    
  54.             byte[] bytes = bos.toByteArray();    
  55.             return defineClass(bytes, 0, bytes.length);    
  56.                 
  57.         } catch (Exception e) {    
  58.             // TODO Auto-generated catch block     
  59.             e.printStackTrace();    
  60.         }    
  61.         //如果没找到类,则用父级类加载器加载     
  62.         return super.findClass(name);    
  63.     }    
  64.     //构造函数     
  65.     public MyClassLoader(){}    
  66.     public MyClassLoader(String classDir){    
  67.         this.classDir = classDir;    
  68.     }    
  69. }   
------- android培训java培训、期待与您交流! ----------
0 0