ClassLoader类加载器

先看例子:

public class ClassLoaderTest{
    public static void main(String[] args) {
        ClassLoader cl = ClassLoaderTest.class.getClassLoader();
        System.out.println(cl);
        System.out.println(cl.getParent());
        System.out.println(cl.getParent().getParent());
        /**
         * List是rt.jar包下
         */
        System.out.println(List.class.getClassLoader());
    }
}

输出结果:

[email protected]
[email protected]
null
null

系统默认三个类加载器

BootStrap(根加载器):java核心库(rt.jar),C/C++写,故而打印出 null

ExtClassLoader(扩展加载器):ext包下

AppClassLoader:classpath下


类加载器的委托机制:

当Java虚拟机要加载第一个类的时候,到底派出哪个类加载器去加载呢?

(1). 首先当前线程的类加载器去加载线程中的第一个类(当前线程的类加载器:Thread类中有一个get/setContextClassLoader(ClassLoader cl);方法,可以获取/指定本线程中的类加载器)

(2). 如果类A中引用了类B,Java虚拟机将使用加载类A的类加载器来加载类B

(3). 还可以直接调用ClassLoader.loadClass(String className)方法来指定某个类加载器去加载某个类

每个类加载器加载类时,又先委托给其上级类加载器当所有祖宗类加载器没有加载到类,回到发起者类加载器,还加载不了,则会抛出ClassNotFoundException,不是再去找发起者类加载器的儿子,因为没有getChild()方法。例如:如上图所示: MyClassLoader->AppClassLoader->ExtClassLoader->BootStrap.自定定义的MyClassLoader1首先会先委托给AppClassLoader,AppClassLoader会委托给ExtClassLoader,ExtClassLoader会委托给BootStrap,这时候BootStrap就去加载,如果加载成功,就结束了。如果加载失败,就交给ExtClassLoader去加载,如果ExtClassLoader加载成功了,就结束了,如果加载失败就交给AppClassLoader加载,如果加载成功,就结束了,如果加载失败,就交给自定义的MyClassLoader1类加载器加载,如果加载失败,就报ClassNotFoundException异常,结束。

在看一个例子 :

public class MyClassLoader extends ClassLoader{
    protected MyClassLoader(ClassLoader parent) {
        super(parent);
    }
    protected MyClassLoader() {
        super();
    }
}

public class ClassLoaderTest2 {
    public static void main(String[] args) {
        MyClassLoader ml1 = new MyClassLoader();
        System.out.println(ml1.getParent());
        MyClassLoader ml2 = new MyClassLoader(Thread.currentThread().getContextClassLoader().getParent());
        System.out.println(ml2.getParent());
    }
}

打印结果:

[email protected]
[email protected]

说明自定义类加载器的父加载器默认是 :AppClassLoader


自定义的类加载器必须继承抽象类ClassLoader

一般只需 重写findClass方法,其实他内部还有一个loadClass方法和defineClass方法;

看loadClass源码:

public Class<?> loadClass(String name) throws ClassNotFoundException {
    return loadClass(name, false);
}

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 {
                //如果有parent,就让parent加载,如果找不到该类,
                //抛ClassNotFoundException ,让子加载器加载,直到加载成功结束
                if (parent != null) {
                    c = parent.loadClass(name, false);
                } else {
                    //如果自定义加载器parent为null,直接用根加载器加载
                    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) {
            解析class文件
            resolveClass(c);
        }
        return c;
    }
}

//该方法直接抛出异常,就是为了重写,所以自定义加载器只需要重写findClass,如果重写
// load方法,还需要重新写容器的委托逻辑, 没有必要
protected Class<?> findClass(String name) throws ClassNotFoundException {
    throw new ClassNotFoundException(name);
}

defineClass这个方法很简单就是将class文件的字节数组编程一个class对象,这个方法肯定不能重写,内部实现是在C/C++代码中实现的

自定义类加载器:

public class MyClassLoader extends ClassLoader{
    private String base_dir;

    protected MyClassLoader(ClassLoader parent) {
        super(parent);
    }
    protected MyClassLoader() {
        super();
    }
    public MyClassLoader(ClassLoader parent,String base_dir) {
            super(parent);
            this.base_dir = base_dir;
        }

    public MyClassLoader(String base_dir) {
        super();
        this.base_dir = base_dir;
    }

    //重写findClass
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        String path = this.base_dir+"\\"+name+".class";
        byte[] b = new byte[0];
        try {
            b = toByteArray(path);
        } catch (IOException e) {
            e.printStackTrace();
        }
        if(b.length == 0){
            return null;
        }
        //定义class信息,把class文件字节码信息组装成jvm的class信息
        return defineClass(null,b,0,b.length);
    }

    private byte[] toByteArray(String filename) throws IOException {

        File f = new File(filename);
        if (!f.exists()) {
            throw new FileNotFoundException(filename);
        }

        ByteArrayOutputStream bos = new ByteArrayOutputStream((int) f.length());
        BufferedInputStream in = null;
        try {
            in = new BufferedInputStream(new FileInputStream(f));
            int buf_size = 1024;
            byte[] buffer = new byte[buf_size];
            int len = 0;
            while (-1 != (len = in.read(buffer, 0, buf_size))) {
                bos.write(buffer, 0, len);
            }
            return bos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
            throw e;
        } finally {
            try {
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            bos.close();
        }
    }

}

public class ClassLoaderTest3 {
    public static void main(String[] args) {
        MyClassLoader ml = new MyClassLoader("E:\\workspace\\mytest\\temp");
        try {
            Class helloClass = ml.loadClass("Hello");
            Object hello =  helloClass.newInstance();
            System.out.println(hello.getClass().getClassLoader());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

    }
}

打印结果:

[email protected]

这里要声明:很容易打印出 AppClassLoader,这是由于你本地idea创建的Hello.class文件可能存在 项目class目录下,删除,从其他目录引入 即可;

现在来测试下自定义的类加载器:

public class Hello {
    public String sayHello(){
        return "hello classLoader";
    }
}

public class ClassLoaderTest3 {
    public static void main(String[] args) {
        //指定根路径,可以根据实际情况引入
        MyClassLoader ml = new MyClassLoader(ClassLoader.getSystemClassLoader().getParent(),"E:\\workspace\\mytest\\temp");
        try {
            //加载
            Class helloClass = ml.loadClass("Hello");
            //初始化
            Object hello =  helloClass.newInstance();
            //调用  加载类的方法
            Object info = helloClass.getMethod("sayHello").invoke(hello);
            System.out.println(info);
            System.out.println(hello.getClass().getClassLoader());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

打印结果:

hello classLoader
[email protected]


时间: 2024-08-26 13:31:48

ClassLoader类加载器的相关文章

Java中对于ClassLoader类加载器 嵌套了深度技术的价值

关于Java技术是一种不断兴起的编程语言,对于ClassLoader 是 Java 届最为神秘的技术之一,无数人被它伤透了脑筋,摸不清门道究竟在哪里.本文我带你彻底吃透 ClassLoader,让你甚至Java类加载器的神奇之处  1.ClassLoader 做什么的? 顾名思义,它是用来加载 Class 的.它负责将 Class 的字节码形式转换成内存形式的 Class 对象.字节码可以来自于磁盘文件 *.class,也可以是 jar 包里的 *.class,也可以来自远程服务器提供的字节流,

java 反射,类的加载过程以及Classloader类加载器

首先自定义一个类Person package reflection; public class Person { private String name; public int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int a

Java基础之ClassLoader类加载器简介

classloader简介 1 classloader层次结构(父子关系) Bootstrap(ClassLoader) ExtClassLoader AppClassLoader XXXClassLoader 详见代码如下: sun.misc.Launcher public Launcher() { Launcher.ExtClassLoader var1; try { var1 = Launcher.ExtClassLoader.getExtClassLoader(); } catch (I

深入源码看java类加载器ClassLoader

ClassLoader类加载器是负责加载类的对象.ClassLoader 类是一个抽象类.如果给定类的二进制名称(即为包名加类名的全称),那么类加载器会试图查找或生成构成类定义的数据.一般策略是将名称转换为某个文件名,然后从文件系统读取该名称的"类文件".java.lang.ClassLoader类的基本职责就是根据一个指定的类的名称,找到或者生成其对应的字节代码,然后从这些字节代码中定义出一个 Java 类,即 java.lang.Class类的一个实例.除此之外,ClassLoad

JVM类加载器ClassLoader解读

ClassLoader类加载器负责将类加载进入JVM中. ClassLoader的作用 (1)加载class文件进入JVM (2)审查每个类应该由谁加载,采用双亲委托机制 (3)将class字节码重新解析成JVM要求的对象格式 ClassLoader结构分析 protected final Class<?> defineClass(byte[] b, int off, int len)throws ClassFormatError{ return defineClass(null, b, of

java的类加载器ClassLoader

类在执行之前会执行三个步骤:加载 ->   连接  -> 初始化 1.java中类的加载 java 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个Class对象,用来封装类在方法区的数据结构.可以把堆区的Class理解为方法区的一面镜子,对应方法区的类的数据结构,通过这面镜子实现类的调用. 加载.class文件的多种方式: 1.从本地系统中直接加载 2.通过网络下载.class文件 3.从zip,jar里加载.class文件

java自定义类加载器

前言 java反射,最常用的Class.forName()方法.做毕设的时候,接收到代码字符串,通过 JavaCompiler将代码字符串生成A.class文件(存放在classpath下,也就是eclipse项目中的bin目录里),然后通过java反射机制,获取main方法并执行..class文件名称固定.当 A.class文件更新的时候,问题出现了,main方法的执行结果总和第一次的执行结果相同. 程序流程 代码提交->接收代码->编译成A.class文件->java反射->m

Java虚拟机笔记 – JVM 自定义的类加载器的实现和使用2

1.用户自定义的类加载器: 要创建用户自己的类加载器,只需要扩展java.lang.ClassLoader类,然后覆盖它的findClass(String name)方法即可,该方法根据参数指定类的名字,返回对应的Class对象的引用. findClass protected Class<?> findClass(String name) throws ClassNotFoundException 使用指定的二进制名称查找类.此方法应该被类加载器的实现重写,该实现按照委托模型来加载类.在通过父

java类加载器学习2——自定义类加载器和父类委托机制带来的问题

一.自定义类加载器的一般步骤 Java的类加载器自从JDK1.2开始便引入了一条机制叫做父类委托机制.一个类需要被加载的时候,JVM先会调用他的父类加载器进行加载,父类调用父类的父类,一直到顶级类加载器.如果父类加载器加载不了,依次再使用其子类进行加载.当然这类所说的父类加载器,不一定他们之间是继承的关系,有可能仅仅是包装的关系. Java之所以出现这条机制,因为是处于安全性考虑.害怕用户自己定义class文件然后自己写一个类加载器来加载原本应该是JVM自己加载的类.这样会是JVM虚拟机混乱或者