博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java 基础:ClassLoader理解和实现
阅读量:4280 次
发布时间:2019-05-27

本文共 6970 字,大约阅读时间需要 23 分钟。

一、定义和作用

ClassLoader是Java的核心组件,主要工作在Class装载的加载阶段。所有的Class都是由ClassLoader进行加载的,ClassLoader 负责通过将Class文件里的二进制数据流装载进系统,然后交给Java虚拟机进行连接、初始化等操作。

二、分类

1.BootStrapClassLoader

作用:C++编写而成, 它是最顶层的类加载器,已经内嵌到JVM中了。在JVM启动时会初始化该ClassLoader,它主要用来读取Java的核心类库JRE/lib/rt.jar中所有的class文件,这个jar文件中包含了java规范定义的所有接口及实现。

2.ExtClassLoader

(1)作用

Java编写,它是用来加载Java的扩展类库javax.*,如读取JRE/lib/ext/*.jar中的包等(这里要注意,有些版本的是没有ext这个目录的)。

(2)源码-》查看加载文件路径:

static class ExtClassLoader extends URLClassLoader {…private static File[] getExtDirs() {    String var0 = System.getProperty("java.ext.dirs");    File[] var1;    if (var0 != null) {        StringTokenizer var2 = new StringTokenizer(var0, File.pathSeparator);        int var3 = var2.countTokens();        var1 = new File[var3];        for(int var4 = 0; var4 < var3; ++var4) {            var1[var4] = new File(var2.nextToken());        }    } else {        var1 = new File[0];    }    return var1;}…}

Idea中查看主要类目录

public class test {    public static void main(String[] args) {        System.out.println(System.getProperty("java.ext.dirs"));    }}

结果:

C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext;C:\WINDOWS\Sun\Java\lib\ext

3.AppClassLoader

(1)作用

Java编写,它是用来读取CLASSPATH下指定的所有jar包或目录的类文件,一般情况下这个就是程序中默认的类加载器。

(2)源码-》查看加载文件路径:

static class AppClassLoader extends URLClassLoader {public static ClassLoader getAppClassLoader(final ClassLoader var0) throws IOException {    final String var1 = System.getProperty("java.class.path");    final File[] var2 = var1 == null ? new File[0] : Launcher.getClassPath(var1);    return (ClassLoader)AccessController.doPrivileged(new PrivilegedAction
() {        public Launcher.AppClassLoader run() {            URL[] var1x = var1 == null ? new URL[0] : Launcher.pathToURLs(var2);            return new Launcher.AppClassLoader(var1x, var0);        }    });}}

文件加载路径为:System.getProperty("java.class.path")

package com.spring.ioc.c5;public class test {    public static void main(String[] args) {         System.out.println(System.getProperty("java.class.path"));    }}

路径:主要是...\javabase\target\classes下面的编译好的包和类

C:\Program Files\Java\jdk1.8.0_191\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\cldrdata.jar;C:\Program…

E:\02LocalProject\javabase\target\classes;
….

4.自定义ClassLoader

定制化开发,下面可以代码实现

5.Loader的双亲委派机制

(1)作用:可以避免同样的字节码加载多次到内存中,一次加载到内存中,如果后续需要,则直接取出使用,不需要,重复加载第二次。

(2)自下而上,检测是否已经加载,如果加载过,则直接调用该class;如果没有加载过,则采用自下而上重新加载。

(3)自上而下,如果寻找对应class文件,有则加载

 

(4)源码

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; } }

三、ClassLoader关键方法以及层次关系

1. loadClass

作用:ClassLoader加载类的入口,此方法负责加载指定名字的类。ClassLoader的实现方法为先从已经加载的类中寻找,如没有则继续从父ClassLoader中寻找,如仍然没找到,则从BootstrapClassLoader中寻找,最后再调用findClass方法来寻找。

2.findClass

作用:查找二进制class文件,并且加载返回一个对象

源码:

protected Class
findClass(String name) throws ClassNotFoundException {    throw new ClassNotFoundException(name);}

3. defineClass

作用:读取字节流,返回类对象

源码:

protected final Class
defineClass(String name, byte[] b, int off, int len)    throws ClassFormatError{    return defineClass(name, b, off, len, null);}

4.三者关系

 

四、自定义classloader代码

1.写一个测试类InputClass.java

package com.spring.ioc.c5;public class InputClass {    static {        System.out.println("Hello World! I am InputClass");    }}

使用idea的终端Terminal在当前目录下编译为InputClass.class文件

…\javabase\src\main\java\com\spring\ioc\c5>javac InputClass.java
生成…\javabase\src\main\java\com\spring\ioc\c5\InputClass.class

2.实现自定义加载类ClassLoaderDIY.java

package com.spring.ioc.c5;import java.io.*;public class ClassLoaderDIY extends ClassLoader{    private String path; //加载class的路径(不包含文件名称)    private String classLoaderName; //加载的class名称    public ClassLoaderDIY(String path, String classLoaderName) {        this.path = path;        this.classLoaderName = classLoaderName;    }    //寻找类文件,并且以二进制数组方式加载进来    @Override    protected Class
findClass(String name) throws ClassNotFoundException {        byte[] b=loadClassData(name);        return defineClass(name,b,0,b.length);    }    //用于加载类文件    private byte[] loadClassData(String name) {        name=path+name+".class";  //即:路径+名称        InputStream in=null;        ByteArrayOutputStream out=null;        try {            in=new FileInputStream(new File(name));            out=new ByteArrayOutputStream();            int i=0;            while ((i=in.read())!=-1){                out.write(i);            }        } catch (Exception e) {            e.printStackTrace();        } finally {            try {                out.close();                in.close();            } catch (IOException e) {                e.printStackTrace();            }        }        return out.toByteArray();    }}

3.测试类ClassLoadTest.java

package com.spring.ioc.c5;public class ClassLoadTest {    public static void main(String[] args) {        ClassLoaderDIY classLoaderDIY=new ClassLoaderDIY("src\\main\\java\\com\\spring\\ioc\\c5\\","FirstClassLoader");        Class c= null;        try {            c = classLoaderDIY.loadClass("com.spring.ioc.c5.InputClass");            System.out.println(c.getClassLoader());            c.newInstance();        } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {            e.printStackTrace();        }    }}

注意:因为加载类,在com.spring.ioc.c5下面,所以,加载类时需要加载指定包目录:com.spring.ioc.c5.InputClass,具体参考:

4.测试结果

加载成功

sun.misc.Launcher$AppClassLoader@18b4aac2

Hello World! I am InputClass

五、参考

1. https://blog.csdn.net/tonytfjing/article/details/47212291

 

你可能感兴趣的文章
mmap
查看>>
How to initialize an array in C
查看>>
DRAM test code
查看>>
daemon to monitor firm upgrade
查看>>
第一個gtestcode
查看>>
Google gTest
查看>>
c++ thread and mutex(c++11)
查看>>
effective c++系列勘誤
查看>>
Clean Code閱讀筆記
查看>>
字符串解析出int和bool等类型的值
查看>>
查看".rmp"內容
查看>>
make jffs2
查看>>
windows上的Terminal
查看>>
HxDSetupEN 分析binary file超好用(window)
查看>>
講解TS
查看>>
Tutorial:Streaming using VLC
查看>>
DVB
查看>>
javascript
查看>>
SUID/SGID/SBIT 權限設定
查看>>
sctp协议在ubuntu12.04中怎样开启
查看>>