Java类加载器

Java中由类字节码流转化为JVM运行时类数据必须使用类加载器进行加载,Java中提供了三个类加载器:根类加载器,扩展类加载器,应用程序类加载器,使用的机制可以概括为“全盘负责双亲委托”机制。

这里写图片描述

注意图中的关系是委派关系,不是继承关系!源码中使用组合实现,即ClassLoader类的parent成员变量,而最上面的那个类加载器(根类加载器)的parent为null。



双亲委托机制在代码中具体体现在ClassLoader类的loadClass方法(下面是JDK1.8代码):

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();
                    //由类全限定名得到Class对象,自定义类加载器(仅)需要重写该方法
                    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;
        }
    }


自定义类加载器


public class MyClassLoader extends ClassLoader {

    public MyClassLoader() {    
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        //这里类字节流来源为class文件
        String path = "C:\\Users\\mao\\Desktop\\" + name + ".class";
        //读取class文件,转化为字节流
        byte[] b = IoUtils.getBytes(path);
        return defineClass(name, b, 0, b == null ? 0 : b.length);
    }
}

这样就定义了一个自己的ClassLoader,接下来为测试代码,在C:\Users\mao\Desktop\路径下放一个String.class文件,然后使用该ClassLoader加载的一个class文件:

public class Test {

    public static void main(String[] args) throws Exception {
        //没有指定父类加载器,则会被默认设置为应用程序类加载器为父类加载器
        ClassLoader cl = new MyClassLoader();
        Class<?> clazz = Class.forName("String", true, cl);
        Object obj = clazz.newInstance();
        System.out.println(obj);
    }
}

代码打印:

I am a custom String class

因为我们放在C:\Users\mao\Desktop\路径下的String类的toString方法被重写了:

public class String {

    @Override
    public java.lang.String toString() {
        return "I am a custom String class";
    }
}

注意:这个String类是我们自己定义的,而JDK内部也有一个String类,运行时这两个类当然不是等价的,因此至少类加载器不同(当然上面的例子类全限定名也不同),而我们是无法做到把JDK的String类替换成我们自己定义的String类的(像上面的例子根类加载器会拒绝加载),这也说明了双亲委派机制的一个优点就是安全性高。



另外,Thread类有一个成员变量contextClassLoader,表示线程上下文类加载器,可以通过setContextClassLoader和getContextClassLoader方法设置和获取该变量的值,默认情况下contextClassLoader的值为父线程的contextClassLoader的值,而Java中最顶层的线程的contextClassLoader为应用程序类加载器。这个是在JDK1.2开始引入的,为了解决“顶层类”需要调用“底层类”却识别不了的问题。

本博客已停止更新,转移到微信公众号上写文章,欢迎关注:Android进阶驿站
Android进阶驿站

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值