• 黑马程序员——【Java高新技术】——类加载器


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

    一、概述

      (一)类加载器(class loader

      用来动态加载Java类的工具,它本身也是Java类。

      (二)类加载器作用

      负责加载 Java 类的字节代码到 Java 虚拟机中。

      (三)Java类加载器

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

      (1)BootStrap(bootstrap class loader):它用来加载 Java 的核心库,是用原生代码来实现的,并不继承自 java.lang.ClassLoader。

      (2)ExtClassLoader(extensions class loader):它用来加载 Java 的扩展库。Java 虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。

      (3)AppClassLoader(system class loader):它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通过 ClassLoader.getSystemClassLoader()来获取它。

      2、BootStrap

      类加载器也是Java类,因为其他java类的类加载器本身也要被类加载器加载,显然必须有第一个类加载器不是不是java类,这正是BootStrap。

      3、类加载器演示,代码示例: 

    1 public class ClassLoaderDemo {
    2     public static void main(String[] args) {
    3         System.out.println(
    4                 ClassLoaderDemo.class.getClassLoader().getClass().getName()
    5                 );//sun.misc.Launcher$AppClassLoader,表示由AppClassLoader加载
    6         System.out.println(System.class.getClassLoader());//null,表示System这个类时由RootStrap加载的
    7     }
    8 }

      (四)类加载器的树状组织结构及管辖范围

      演示类加载器的树状组织结构,代码示例如下:

    1 public class ClassLoaderTree {
    2    public static void main(String[] args) {
    3        ClassLoader loader = ClassLoaderTree.class.getClassLoader();
    4        while (loader != null) {
    5             System.out.println(loader.toString());
    6             loader = loader.getParent();
    7        }
    8    }
    9 }

    二、类加载器的委托机制

      (一)加载类的过程

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

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

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

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

      (二)类加载器的委托

      1、每个类加载器加载类时,又先委托给上级类加载器。

      (1)首先,类加载器发起者一级级向上委托,直至BootStrap;

      (2)从BootStrap加载器开始查找,找到了直接返回,没找到再一级级向下返回让其子级加载器查找;

      (3)一直返回到发起者,发起者再没找到,则抛出ClassNotFoundException,而不再找发起者类加载器的子类来找。

      2、面试题:可不可以自己写一个java.lang.System类呢?

      (1)通常是不可以的,由于类加载器的委托机制,会先将System这个类一级级委托给最顶级的BootStrap,由于BootStrap在其指定的目录中加载的是rt.jar中的类,rt.jar中有System类,那么就会直接加载,而不会去加载自定义的System类。

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

    三、自定义类加载器

      (一)概述

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

      2、类加载器的原理

      (1)loadClass()内部会先委托给父级,当父级找不到后返回,再调用findClass(String name)方法,即自定义的类加载器去找。所以只需要覆写findClass方法,就能实现用自定义的类加载器加载类的目的。

      (2)一般自定义类加载器,会把需要加载的类放在自己指定的目录中,而java中已有的类加载器是不知道你这个目录的,所以会找不到。这样才会调用你复写的findClass()方法,用你自定义的类加载器去指定的目录加载类。

      3、模板方法设计模式

      模板方法设计模式,保留了loadClass()方法中的流程(此流程就是先找父级,找不到再调用自定义的类加载器),因此只需覆盖findClass()方法,实现局部细节就行了。

      4、当得到了class文件中的二进制数据,如何将class文件的内容转换成字节码?

      ClassLoader提供了一个protected Class<?> defineClass(String name , byte[] b , int off , int len)方法,只需要将类的class文件传入,就可以将其变为字节码。

      (二)编程步骤

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

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

      3、编写一个程序,调用类加载器加载类,在源程序中不能用该类名定义引用变量,因为编译器无法识别这个类,程序中除了可使用ClassLoader的loadClass方法外,还可以使用设置线程的上下文类加载器或系统类加载器,然后再使用Class.forName。

      (三)编码步骤

      1、对不带包名的class文件进行加密,加密结果存放到另外一个目录,例如: java MyClassLoader MyTest.class F:itcast

      2、运行加载类的程序,结果能够被正常加载,但打印出来的类装载器名称为AppClassLoader:java MyClassLoader MyTest F:itcast

      3、用加密后的类文件替换CLASSPATH环境下的类文件,再执行上一步操作就出问题了,错误说明是AppClassLoader类装载器装载失败。

      4、删除CLASSPATH环境下的类文件,再执行上一步操作就没问题了。

      (四)自定义类加载器,代码实现(张孝祥老师讲解)

      1、定义一个测试类:ClassLoaderAttachment

    1 // 定义一个测试类,继承Date,用于加载
    2 import java.util.Date;
    3 public class ClassLoaderAttachment extends Date{
    4     public String toString(){
    5         return " hello,itcast";
    6     }
    7 }

      2、自定义类加载器:MyClassLoader,继承ClassLoader,覆盖findClass()方法。

     1 import java.io.ByteArrayOutputStream;
     2 import java.io.FileInputStream;
     3 import java.io.FileOutputStream;
     4 import java.io.InputStream;
     5 import java.io.OutputStream;
     6 public class MyClassLoader extends ClassLoader{
     7     public static void main(String[] args) throws Exception {
     8         String srcPath = args[0];
     9         String destDir = args[1];
    10         FileInputStream fis = new FileInputStream(srcPath);
    11         String destFileName = srcPath.substring(srcPath.lastIndexOf("\")+1);
    12         String destFilePath = destDir+"\"+destFileName;
    13         FileOutputStream fos = new FileOutputStream(destFilePath);
    14         cypher(fis,fos);//加密class字节码
    15         fis.close();
    16         fos.close();
    17     }
    18     //加密方法 
    19     private static void cypher(InputStream ips,OutputStream ops) throws Exception{
    20         int b = -1;
    21         while((b = ips.read())!=-1){
    22             ops.write(b^0xff);
    23         }
    24     }
    25 
    26 private String classDir;
    27 
    28    @Override
    29    protected Class<?> findClass(String name) throws ClassNotFoundException {
    30     String classFileName = classDir + "\"  + name.substring(name.lastIndexOf('.')+1) + ".class";
    31     try {
    32         FileInputStream fis = new FileInputStream(classFileName);
    33         ByteArrayOutputStream bos = new ByteArrayOutputStream();
    34         cypher(fis,bos);//解密
    35         fis.close();
    36         System.out.println("aaa");
    37         byte[] bytes = bos.toByteArray();
    38         return defineClass(bytes, 0, bytes.length);
    39     } catch (Exception e) {
    40         e.printStackTrace();
    41     }
    42     return null;
    43    }
    44     
    45    public MyClassLoader(){}
    46    public MyClassLoader(String classDir){
    47     this.classDir = classDir;
    48    }
    49 }

      3、定义类加载测试类:ClassLoaderTest,测试自定义的类加载器

    1 import java.util.Date;
    2 public class ClassLoaderTest {
    3     public static void main(String[] args) throws Exception {
    4         Class clazz = new MyClassLoader (" itcastlib " ) . loadClass ( " cn.itcast . day2 . ClassLoaderAttachment " ) ;
    5         Date d1 = (Date)clazz.newInstance();
    6         System.out.println(d1);
    7     }
    8 }

     

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

  • 相关阅读:
    webApp 开发技术要点总结
    前端好难
    webApp前端开发技巧总结
    WAP、触屏版网站及APP的区别
    ajax 200 4 parseerror 的错误
    .Net Windows Service(服务) 调试安装及System.Timers.Timer 使用
    引用:WebAPI中的定时处理-使用Quartz.Net
    Redis 应该是存放的数据超出了范围
    easyui Dialog 去边框
    MSSQL SQL Server代理 作业 设置(调用存储过程)
  • 原文地址:https://www.cnblogs.com/jianxingjianyuan2014/p/4013312.html
Copyright © 2020-2023  润新知