黑马程序员 java高新技术 类加载器

来源:互联网 发布:Linux改变所有文件权限 编辑:程序博客网 时间:2024/05/01 01:15

---------- android培训、java培训、期待与您交流! ----------


类加载器


   什么是类加载器

   类加载器是 Java 语言的一个创新,也是 Java 语言流行的重要原因之一。它使得 Java 类可以被动态加载到 Java 虚拟机中并执行。类加载器从 JDK 1.0 就出现了,最初是为了满足 Java Applet 的需要而开发出来的。Java Applet 需要从远程下载 Java 类文件到浏览器中并执行。现在类加载器在 Web 容器和 OSGi 中得到了广泛的使用。一般来说,Java 应用的开发人员不需要直接同类加载器进行交互。Java 虚拟机默认的行为就已经足够满足大多数情况的需求了。不过如果遇到了需要与类加载器进行交互的情况,而对类加载器的机制又不是很了解的话,就很容易花大量的时间去调试 ClassNotFoundException和 NoClassDefFoundError等异常。Java 的类加载器是Java 语言中的这个重要概念

   

   类加载器作用

    类加载就是加载类的工具

   我们在Java程序里面用到一个类,如System,出现了这个类的名字,那么Java虚拟机会把这个类的字节码加载到内存里面来

   通常这些类的字节码放在硬盘里面的classPath指定的目录下。我们把这个类加载到内存里面来,再对它进行处理

   处理完的结果就是2进制字节码,这些加载和处理的工作就是类加载器要做的事情,这就是类加载器的作用

 

  1、Java虚拟机中可以安装多个类加载器,系统默认三个主要类加载器

     每个类负责加载特定位置的类:BootStrap,ExtClassLoader,AppClassLoader。

  2、类加载器也是Java类,因为其他是java类的类加载器本身也要被类加载器加载,

     显然必须有第一个类加载器不是不是java类,这正是BootStrap

  3、Java虚拟机中的所有类装载器采用具有父子关系的树形结构进行组织,在实例化每个类装载器对象时,

     需要为其指定一个父级类装载器对象或者默认采用系统类装载器为其父级类加载。



package cn.itcast.text2;  import java.util.Date;  public class ClassLoadTest{      public static void main(String[] args) throws Exception{          System.out.println(                  ClassLoadTest.class.getClassLoader().                  getClass().getName());//为AppClassLoader          System.out.println(                  System.class.getClassLoader());//为null      }  }  

  类加载器委托机制

   加载类的方式

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

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

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

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

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

  每个ClassLoader本身只能分别加载特定位置和目录中的类,但他们可以委托其他类的加载器去加载,这就是类加载器的委托模式

  类加载器一级级委托到BootStrap类加载器,当BootStrap在指定目录中没有找到要加载的类时,无法加载当前所要加载的类

  就会一级级返回子孙类加载器,进行真正的加载,每级都会先到自己相应指定的目录中去找,有没有当前的类;

  直到退回到最初的类装载器的发起者时

  如果它自身还未找到,未完成类的加载,那就报告ClassNoFoundException的异常。

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

   直到发起者,再没找到就报异常。

 

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


  自定义类加载器

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

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

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

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

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


   编程步骤:

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

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

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

  主函数 加载器

package cn.itcast.day2;    import java.util.Date;    public class ClassLoaderTest {        public static void main(String[] args)throws Exception {          // TODO Auto-generated method stub          //获取本类字节码-->获取加载器-->获取加载器的字节码-->获取加载器的名字          System.out.println("获取类加载器的名字:"+                  ClassLoaderTest.class.getClassLoader().getClass().getName());          System.out.println("获取System类加载器的名字:"+          System.class.getClassLoader());                    //查看类加载器的委托机制          ClassLoader loader = ClassLoaderTest.class.getClassLoader();          while(loader !=null){              System.out.println(loader.getClass().getName());              loader = loader.getParent();          }          System.out.println(loader);                    //System.out.println(new ClassLoaderAttachment());                    <span style="color:#333333;">          </span><span style="color:#ff0000;">        //解密加载          </span><span style="color:#333333;">        //1。创建MyClassLoader类,传入一个文件夹目录,并加载这个文件夹下面文件,得到这个类的字节码          Class clazz = new MyClassLoader("itcastlib").loadClass("ClassLoaderAttachment");          //2。将这个类实例化//注意不能用"ClassLoaderAttachment"接收,          //因为这个类本身就是加密的,所以编译器是不允许加载的,所以我们要用Date(父类)进行接收类型          //ClassLoaderAttachment d1 = (ClassLoaderAttachment)clazz.newInstance();//这样不可以          Date d1 = (Date)clazz.newInstance();          System.out.println(d1);      }  } 


 自定义类加载器

 

public class MyClassLoader extends ClassLoader {        public static void main(String[] args)throws Exception {          // 1.创建文件路径输入和输出          String srcPath = args[0];//源路径          String destDir = args[1];//目的          FileInputStream fis = new FileInputStream(srcPath);//传入相对路径          String destFileName = srcPath.substring(srcPath.lastIndexOf('\\')+1);//得到执行文件的名字          String destPath = destDir+"\\"+destFileName;//得到目的完整路径          FileOutputStream fos = new FileOutputStream(destPath);//输出          cypher(fis,fos);          fis.close();          fos.close();      }            //加密方法      private static void cypher(InputStream ips,OutputStream ops)throws Exception{          int b = 0;          while((b = ips.read())!=-1){              ops.write(b ^ 0xff);//进行异或运算加密          }      }      private String ClassDir;            //覆盖ClassLoader方法      @Override       //findClass方法接收的是一个类名      protected Class<?> findClass(String name) throws ClassNotFoundException {          // TODO Auto-generated method stub          String classFileName = ClassDir + "\\" + name + ".class";//获取完整路径          try {              FileInputStream fis = new FileInputStream(classFileName);//读取文件              ByteArrayOutputStream bos = new ByteArrayOutputStream();//使用字节数组输出文件              cypher(fis, bos);//调用cypher方法读取和输出文件              fis.close();              byte[] bytes = bos.toByteArray();//將字節轉成字節數組              return defineClass(bytes, 0, bytes.length);//调用defineClass解释方法,将字节数组写入Class文件          } catch (Exception e) {              // TODO Auto-generated catch block              e.printStackTrace();          }                    return super.findClass(name);//上面的return 搞不定,否则就返回父类      }      //构造函数      public MyClassLoader(){                }      public MyClassLoader(String ClassDir){//这里接收的是另外一个主函数传递过来的参数          this.ClassDir = ClassDir;      }    }

  

  对自定义加载文件解密

  想要对自定义加载文件解密,就要用到我们自定义的加载器,所以我们要继承ClassLoader加载器

   1.继承ClassLoader

   2.覆盖它的findClass方法

 



  


0 0