黑马程序员:对Java类加载器体系的认识

来源:互联网 发布:linux查看服务状态 编辑:程序博客网 时间:2024/05/03 14:21

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


类加载器的作用是可以使Java 类可以被动态加载到 Java 虚拟机中并执行。

类加载器(class loader)用来加载 Java 类到 Java 虚拟机中。一般来说,Java 虚拟机使用 Java 类的方式如下:Java 源程序(.java 文件)在经过 Java 编译器编译之后就被转换成 Java 字节代码(.class 文件)。类加载器负责读取 Java 字节代码,并转换成 java.lang.Class 类的一个实例。每个这样的实例用来表示一个 Java 类。通过此实例的 newInstance()方法就可以创建出该类的一个对象。

Java虚拟机中可以安装多个类加载器,系统默认三个主要类加载器,每个类负责加载特定位置的类:BootStrap、ExtClassLoader、AppClassLoader类。加载器也是Java类,因为Java类本身要被加载器加载,显然必须有第一个类加载器不是Java类,这个是BootStrap。Java虚拟机中的所有类装载器采用具有父子关系的树形结构进行组织,在实例化每个类装载器对象时,需要为其指定一个父级类装载器对象或者默认采用系统类装载器为其父级类加载。

java.lang.ClassLoader 类介绍

java.lang.ClassLoader 类的基本职责就是根据一个指定的类的名称,找到或者生成其对应的字节代码,然后从这些字节代码中定义出一个 Java 类,即 java.lang.Class 类的一个实例。除此之外,ClassLoader 还负责加载 Java 应用所需的资源,如图像文件和配置文件等。不过本文只讨论其加载类的功能。

方法说明
  getParent() 返回该类加载器的父类加载器。
  loadClass(String name) 加载名称为 name 的类,返回的结果是 java.lang.Class 类的实例。
  findClass(String name) 查找名称为 name 的类,返回的结果是 java.lang.Class 类的实例。
  findLoadedClass(String name) 查找名称为 name 的已经被加载过的类,返回的结果是 java.lang.Class 类的实例。
  defineClass(String name, byte[] b, int off, int len) 把字节数组 b 中的内容转换成 Java 类,返回的结果是 java.lang.Class 类的实例。这个方法被声明为 final 的。
  resolveClass(Class c) 链接指定的 Java 类。

System.out.println(ClassLoadTest.class.getClassLoader().getClass().getName());System.out.println("-------------");ClassLoader loader = ClassLoadTest.class.getClassLoader();while(loader != null){    System.out.println(loader.getClass().getName());    loader = loader.getParent();}System.out.println(loader);

结果:
sun.misc.Launcher$AppClassLoader
-------------
sun.misc.Launcher$AppClassLoader
sun.misc.Launcher$ExtClassLoader
null

类加载器的树状组织结构


类加载器的委托机制
首先当前线程的类加载器去加载线程中的第一个类。
如果类A引用了类B,Java虚拟机将使用加载类A的类装载器来加载类B。
还可以直接调用ClassLoader.loadClass()方法来制定某个类加载器去加载某个类

每个类加载器加载类时,先返回给其上级类加载器。当所有上级类加载器没有加载到类,回到发起者类加载器,还加载不了,则抛ClassNotFoundException,不是再去找发起者类加载器的儿子,因为没有getChild方法,即使有,那么多个
儿子,也不知道找哪一个。

ClassLoader loader = ClassLoadTest.class.getClassLoader();while(loader != null){    System.out.println(loader.getClass().getName());    loader = loader.getParent();}System.out.println(loader);

结果:
sun.misc.Launcher$AppClassLoader
sun.misc.Launcher$ExtClassLoader
null

面试题:
能不能自己写一个类交java.lang.System.
通常不能,类加载器采用委托机制,这样可以保证爸爸们优先,也就是总是使用爸爸们能找到的类,这样就会自动找到系统提供的System类了。

编写自己的类加载器

知识点讲解:
自定义的类加载器必须继承ClassLoader
loadClass方法与findClass方法
defineClass方法

编程步骤:
编写一个对文件内容进行简单加密的程序。
编写了一个自己的类装载器,可实现对加密过的类进行装载和解密。
编写一个程序调用类加载器加载类,在源程序中不能用该类名定义引用变量,因为编译器无法识别这个类,程序中可以除了使用ClassLoader.load方法之外,还可以使用设置线程的上下文类加载器或者系统类加载器,然后再使用Class.forName。

//模板方法设计模式
父类 --> loadClass(流程)/findClass(细节)/得到class文件的二进制字节码/defineClass
子类1(自己干)
子类2(自己干)
总体的流程在父类里面规定好了,细节定义成抽象方法,子类自己完成。

自定义类代码:
public class ClassLoaderAttrachment extends Date {    @Override    public String toString() {        return "hello, word";    }}

自定义类加载器类:
public class MyClassLoad extends ClassLoader {    public static void main(String[] args) throws IOException {String srcPath = args[0];//srcPath = D:\workspace\BasicEnhance\bin\com\itheima\day7\ClassLoaderAttrachment.classString destDir = args[1];//destDir = myclasslibFileInputStream 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 IOException {int b = -1;while ((b = ips.read()) != -1) {    ops.write(b ^ 0xff);// 在写之前对字节进行异或运算,加密}    }    private String classDir;    public MyClassLoad() {    }    public MyClassLoad(String classDir) {this.classDir = classDir;    }    @Override    protected Class<?> findClass(String name) throws ClassNotFoundException {String classFileName = classDir + "\\" + name + ".class";try {    FileInputStream fis = new FileInputStream(classFileName);    ByteArrayOutputStream bos = new ByteArrayOutputStream();    cypher(fis, bos);    byte[] bytes = bos.toByteArray();    return defineClass(bytes, 0, bytes.length);} catch (Exception e) {    e.printStackTrace();}return super.findClass(name);    }}

得到自定义类字节码文件的代码:
Class clazz = new MyClassLoad("myclasslib").loadClass("ClassLoaderAttrachment");Date d1 = (Date)clazz.newInstance();System.out.println(d1);


原创粉丝点击