Android客户端类加载机制 | 青训营笔记

80 阅读3分钟

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

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

一、概念

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

  • 加载
  • 链接
  • 初始化

类加载器的过程

  类加载器的作用就是从文件系统或者网络中加载Class文件,至于他是否可以运行就不是ClassLoader的工作了。

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;
    }
}
复制代码

五、自定义类加载器

在我们的日常应用程序的开发中,我基本是用不到自定义类加载器的,基本就是由前面介绍的这三个类加载器来相互配合搞定的。但是在有些特殊的情况下我们不希望通过系统的类加载器来处理,这时我们就可以通过自定义类加载来实现了。

  用户自定义类加载器的实现步骤:

  1. 我们可以直接编写 java.lang.ClassLoader 类的实现来完成自定义的处理
  2. 在JDK1.2之前,自定义类加载器时我们总是会继承ClassLoader然后重写loadClass方法,达到自定义类加载的目的,但是在JDK1.2之后建议把自定义的类加载逻辑放在findClass()方法中
  3. 最后在编写自定义类加载的时候,如果没有太过于复杂的需求,可以直接继承URLClassLoader类,这样可以达到简化自定义类加载器的目的。

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