类加载机制 | 青训营笔记

54 阅读3分钟

这是我参与「第四届青训营 」笔记创作活动的第2天

最近做安卓青训营的项目,对如此多的api给震惊到了,好奇java中类加载的机制到底是如何把海量的方法和类找到的。

一、概念

把描述类的数据从 Class 文件加载到内存
该过程包括了

  • 加载
  • 链接
  • 初始化

1. 加载

类加载器(ClassLoader) 把class读入内存创建Class对象

2. 链接

把数据加载到JRE,给数据赋值
主要包括了

  • 验证 --- 验证.class文件字节流是否符合class文件的格式的规范,验证class文件的魔术版本等
  • 准备 --- 对Static变量设置默认值(不是初始值!!)
  • 解析 --- 将类的二进制数据中的符号引用替换成直接引用

3. 初始化

  • 初始化static(把准备阶段的static变量改成初始值)
  • 假如这个类还没有被加载和连接,则程序先加载并连接该类
  • 假如该类的直接父类还没有被初始化,则先初始化其直接父类
  • 假如类中有初始化语句,则系统依次执行这些初始化语句

二、类加载的时机

  1. 初始化一个类时发现父类未加载
  2. new或反射调用的时候
  3. 调用静态方法或静态变量

三、3个类加载器ClassLoader

image.png

1. 启动类加载器

负责加载java核心类库(String等)

2. 扩展类加载器

加载jre中的jar

3. 应用程序类加载器

加载CLASSPATH路径下我们自己写的类


注意:getParent()获取到的父加载器!=父类
关键代码在Launcher.class下

image.png

四、类加载机制(双亲委派)

简述:属实是老生常谈了
三个步骤

  • 查询缓存(已加载过则返回)
  • 未加载过则请求父类去加载
  • 父类加载失败则递归回来该类加载器查看是否能加载

好处

优先使用父加载器,防止自己写个String去替换jdk的String类

例子: 看看ClassLoader下的loadClass方法,这个方法在子类多有重写

protected Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException
{
    synchronized (getClassLoadingLock(name)) {
        // First, check if the class has already been loaded
        //这里底层是调用了c++的搜索类,其实就是搜索缓存
        Class<?> c = findLoadedClass(name);
        if (c == null) {
                if (parent != null) {
                    // 父加载器去加载
                    c = parent.loadClass(name, false);
                } else {
                    // bootstrapclassloader是用c++写的无法使用getParent获取到
                    c = findBootstrapClassOrNull(name);
                }
            } 
            if (c == null) {
                // 父类和缓存都没有加载,要自己去尝试加载了
                c = findClass(name);
            }
        }
        return c;
    }
}

五、自定义类加载器

既然loadclass帮我们处理了双亲委派机制,我们自定义类只需要重写findclass方法,这个方法主要就是使用io读取目的的类

具体要怎么使用,使自己的容器具有特性,可以参考tomcat的类加载实现,防止了new出一个tomcat容器 具体可以拜读cloud.tencent.com/developer/a…