Java 类加载ClassLoader学习心得

本文是笔者在学习Java 类加载的过程当中,整理的心得体会,共勉!java

类加载的意义

简单概念:网络

将Java类的.class文件中的二进制数据读入到内存中,放置在运行时数据区的方法区内。jvm

何时会进行类加载?函数

         本地系统加载;学习

         代理加载,好比Spring的AOP动态代理;测试

         从jar包中加载;this

         等等等等加密

ClassLoader类结构介绍

ClassLoader是一个抽象类,核心方法以下:spa

defineClass():目的是将byte字节流解析成JVM可以识别的Class对象。命令行

findClass():此方法支持重载,与defineClass()配合使用,目的是获取Class对象的字节码。这个方法的意义在于,咱们不只仅能够经过class文件实例化对象,也能够经过其余方式,好比从网络上获取的字节码文件,可能会有对应的加密规则。

loadClass():此方法支持重载,目的是获取加载类的类对象。

resolveClass():实现让JVM连接这个类,此方法调用的是本地方法,不能重载。

Class类的生命周期

 

 

今生命周期对应到ClassLoader类的具体方法:
加载:loadClass()、findClass()、defineClass()

链接:包括验证、准备、解析:resolveClass() 本地native方法

初始化:JVM负责

类加载机制(双亲委托)

概念介绍:

大体的ClassLoader分类:

  1. Bootstrap ClassLoader:负责加载存放在 JDK\jre\lib(JDK表明JDK的安装目录,下同)下,或被 -Xbootclasspath参数指定的路径中的,而且能被虚拟机识别的类库
  2. ExtClassLoader:负责加载 JDK\jre\lib\ext目录中,或者由 java.ext.dirs系统变量指定的路径中的全部类库(如javax.开头的类),开发者能够直接使用扩展类加载器
  3. AppClassLoader:负责加载用户类路径(ClassPath)所指定的类,其父类是ExtClassLoader

心得体会:

  这里的父加载器与子加载器 在java类关系上并非继承关系;

  实现本身的类加载器,不论是直接实现抽象类ClassLoader,仍是继承其余子类,父加载器都是AppClassLoader;

类加载的方式

一、命令行启动应用时候由JVM初始化加载

二、经过Class.forName()方法动态加载

三、经过ClassLoader.loadClass()方法动态加载

Class.forName()ClassLoader.loadClass()区别

Class.forName():将类的.class文件加载到jvm中以外,还会对类进行解释,执行类中的static块;

ClassLoader.loadClass():只干一件事情,就是将.class文件加载到jvm中,不会执行static中的内容,只有在newInstance才会去执行static块。

Class.forName(name,initialize,loader)带参函数也可控制是否加载static块。而且只有调用了newInstance()方法采用调用构造函数,建立类的对象 

应用实践

实现自定义的ClassLoader

基于上面对于类加载器的分析,自定义ClassLoader的状况以下:

  1. 须要在自定义路径下查找自定义的class文件
  2. 对咱们想要加载的类作特殊处理,好比经过网络传输的类的字节码可能进行了加密,获取这种类的字节码须要在咱们自定义的ClassLoader中实现
  3. 实现类的热部署

从上面的分析咱们还能够获得,自定义ClassLoader的实现思路能够经过继承ClassLoader类,重写findClass()来实现,固然你也能够重写loadClass(),可是默认的loadClass()是实现类加载双亲委托模式的,除非你想要破坏这种双亲委托模式,否则就别重写了。

下面是我在本机写的测试自定义ClassLoader的Demo:

/**
 * 自定义路径下的class文件加载
 * @Auther: trey_stao@163.com
 * @Date: 2018/5/31 19:34
 * @Description:
 */
public class PathClassLoader extends ClassLoader {
    private String classPath;

    public PathClassLoader(String classPath) {
        this.classPath = classPath;
    }

    private String packageName = "com.trey.classloader.Test";

    protected Class<?> findClass(String name) throws ClassNotFoundException {
        if(packageName.startsWith(name)) {
            byte[] classData = getData(name);
            if(classData == null) {
                throw new ClassNotFoundException();
            } else {
                System.out.println("当前类加载器:"+this.getClass().getClassLoader().toString());
                return defineClass(name, classData, 0, classData.length);
            }
        } else {
            return super.loadClass(name);
        }
    }

    private byte[] getData(String name) {
        String path = classPath + File.separatorChar + name.replace('.',File.separatorChar) + ".class";
        try {
            InputStream is = new FileInputStream(path);
            ByteArrayOutputStream stream = new ByteArrayOutputStream();
            byte[] buffer = new byte[2048];
            int num = 0;
            while ((num = is.read(buffer)) != -1) {
                stream.write(buffer, 0, num);
            }
            return stream.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}

 

实现类的热部署

这是给本身布置的对类加载学习的总结练习。下面说下本身准备实现的思路:

  1. 实现一个server服务,监听具体目录下的全部class文件的更新状况
  2. 当出现class文件更新时,使用自定义的classLoader进行加载

 

参考文献

  整理这篇学习心得时,参考了一些文章,特在此注明,表示感谢!

  周志明:《深刻理解Java虚拟机:JVM高级特性与最佳实践》

  纯洁的微笑:java类的加载机制

  许令波:《深刻分析Java Web 技术内幕》

相关文章
相关标签/搜索