Java-ClassLoader详解

一、ClassLoader概念

  1. ClassLoader是用来动态的加载class文件到虚拟机中,并转换成java.lang.class类的一个实例,每个这样的实例用来表示一个java类,我们可以根据Class的实例得到该类的信息,并通过实例的newInstance()方法创建出该类的一个对象,除此之外,ClassLoader还负责加载Java应用所需的资源,如图像文件和配置文件等。

  2. ClassLoader类是一个抽象类。如果给定类的二进制名称,那么类加载器会试图查找或生成构成类定义的数据。一般策略是将名称转换为某个文件名,然后从文件系统读取该名称的“类文件”。ClassLoader类使用委托模型来搜索类和资源。每个 ClassLoader实例都有一个相关的父类加载器。需要查找类或资源时,ClassLoader实例会在试图亲自查找类或资源之前,将搜索类或资源的任务委托给其父类加载器。

  3. 注意:程序在启动的时候,并不会一次性加载程序所要用的所有class文件,而是根据程序的需要,通过Java的类加载机制来动态加载某个class文件到内存中。

二、层次结构

当JVM(Java虚拟机)启动时,会形成由三个类加载器组成的初始类加载器层次结构:

    bootstrap classloader
             |
    extension classloader
             |
    system classloader

三、JVM加载class文件到内存有两种方式

  1. 隐式加载:不通过在代码里调用ClassLoader来加载需要的类,而是通过JVM来自动加载需要的类到内存,例如:当类中继承或者引用某个类时,JVM在解析当前这个类不在内存中时,就会自动将这些类加载到内存中。

  2. 显示加载:在代码中通过ClassLoader类来加载一个类,例如调用this.getClass.getClassLoader().loadClass()或者Class.forName()。

四、ClassLoader加载类的过程

  1. 找到.class文件并把这个文件加载到内存中

  2. 字节码验证,Class类数据结构分析,内存分配和符号表的链接

  3. 类中静态属性和初始化赋值以及静态代码块的执行

五、如何实现自定义的类加载器?

1. 方式一:继承ClassLoader,重写父类的findClass()方法,代码如下:

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
/**
 * @author bilaisheng
 * @date 2018-06-24
 * @version jdk1.8
 * @Tools Idea2018.1.5
 */
public class PathClassLoader extends ClassLoader
{
    // 声明文件路径
    public static final String drive = "D:/";
    // 声明文件类型(后缀)
    public static final String fileType = ".class";
    public static void main(String[] args) throws Exception
    {
        PathClassLoader loader = new PathClassLoader();
        // 此住仅找D盘根目录下HelloWorld.class文件
        // 如何生成Class文件。可以用javac 文件名称生成
        Class<?> objClass = loader.loadClass("HelloWorld", true);
        Object obj = objClass.newInstance();
        System.out.println(objClass.getName());
        System.out.println(objClass.getClassLoader());
        System.out.println(obj.getClass().toString());
    }
    public Class<?> findClass(String name)
    {
        byte[] data = loadClassData(name);
        return defineClass(name, data, 0, data.length);// 将一个 byte 数组转换为 Class// 类的实例
    }
    public byte[] loadClassData(String name)
    {
        FileInputStream fis = null;
        byte[] data = null;
        try
        {
            fis = new FileInputStream(new File(drive + name + fileType));
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            int ch = 0;
            while ((ch = fis.read()) != -1)
            {
                baos.write(ch);
            }
            data = baos.toByteArray();
        } catch (IOException e)
        {
            e.printStackTrace();
        }
        return data;
    }
}

其中下方loadClass方法调用了父类中的loadClass方法

Class<?> objClass = loader.loadClass("HelloWorld", true);

下方为ClassLoader中的loadClass方法源码

protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }
                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    c = findClass(name);
                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

Tips:这个方法首先检查指定class是否已经被加载,如果已被加载过,则调用resolveClass()方法链接指定的类,如果还未加载,则先将搜索类或资源的任务委托给其父类加载器,检查该class是否被BootstrapClassLoader加载,如果上述两步均没有找到加载的class,则调用findClass()方法,在我们自定义的加载器中,我们重写了findClass方法,去我们指定的路径下加载class文件。

Tips:如果一定想要把自定义加载器需要加载的类放在类路径中, 就要把自定义类加载器的父加载器设置为null。

2、 方式二:继承URLClassLoader类,然后设置自定义路径的URL来加载URL下的类。

将指定的目录转换为URL路径,然后重写findClass方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值