类的加载机制

参考资料:
Java 类加载机制(阿里面试题)
从经典面试题看java中类的加载机制
面试官:请你谈谈Java的类加载过程
深入理解Java类加载器(ClassLoader)

目录

  • 类的加载机制
    • 目录
    • 类加载器
      • 启动类加载器
      • 扩展类加载器
      • 应用程序类加载器
      • 自定义类加载器
    • 类加载机制
      • 全盘负责
      • 双亲委派模型
      • 缓存机制
    • 类加载器中的四个重要方法
      • loadClass(String name, boolean resolve)
      • findClass(String name)
      • defineClass(byte[] b, int off, int len)
      • resolveClass(Class≺?≻ c)
    • 类加载过程
      • 加载
      • 连接
        • 验证
        • 准备
        • 解析
      • 初始化
      • 流程图
    • 子类继承父类时的执行顺序

类加载器

类的加载是由类加载器完成的,类加载器包括:启动类加载器(BootStrap)、扩展类加载器(ExtClassLoader)、应用程序类加载器(AppClassLoader)和自定义类加载器(java.lang.ClassLoader的子类)。

启动类加载器

一般用本地代码实现,负责加载JVM基础核心类库,即 JAVA_HOME\lib 目录下的类。

扩展类加载器

继承自启动类加载器,加载 \lib\ext 下的类,或者被 java.ext.dirs 系统变量指定的类。

应用程序类加载器

继承自扩展类加载器,加载 ClassPath 中的类,或者系统变量 java.class.path 所指定的目录中记载类,是用户自定义加载器的默认父加载器。

自定义类加载器

继承自 ClassLoader 类。

为什么要自定义类加载器

一方面是由于java代码很容易被反编译,如果需要对自己的代码加密的话,可以对编译后的代码进行加密,然后再通过实现自己的自定义类加载器进行解密,最后再加载。

另一方面也有可能从非标准的来源加载代码,比如从网络来源,那就需要自己实现一个类加载器,从指定源进行加载。

类加载机制

全盘负责

当一个类加载器负责加载某个 Class 时,该 Class 所依赖的和引用的其他 Class 也将由该类加载器负责载入,除非显式指定另外一个类加载器来载入。

双亲委派模型

如果一个类加载器收到了 Class 加载的请求,它首先不会自己去尝试加载这个 Class ,而是把请求委托给父加载器去完成,依次向上。因此,所有的类加载请求最终都应该被传递到顶层的启动类加载器中,只有当父加载器在它的搜索范围中没有找到所需的 Class 时,即无法完成该加载,子加载器才会尝试自己去加载该 Class 。

这样做的好处是:
1. 避免同一个类被多次加载
2. 安全,Java 核心 API 中定义的类不会被随意替换
3. 每个加载器只能加载自己范围内的类

缓存机制

所有加载过的 Class 都会被缓存,当程序中需要使用某个 Class 时,类加载器先从缓存区寻找该 Class ,只有当缓存区不存在时,系统才会去读取该 Class 对应的二进制数据,并将其转换成 Class 对象,存入缓存区。

这就是为什么修改了 Class 后,必须重启JVM,程序的修改才会生效。

类加载器中的四个重要方法

loadClass(String name, boolean resolve)

protected Class<?> loadClass(String name, boolean resolve)      throws ClassNotFoundException  {      synchronized (getClassLoadingLock(name)) {          // 先从缓存查找该class对象,找到就不用重新加载          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) {                  // 如果都没有找到,则通过自定义实现的findClass去查找并加载                  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;      }  }

流程:
缓存 -> 父类加载器 -> 没有父类 -> 启动类加载器 -> 自己的 findClass() 方法

findClass(String name)

由自己负责加载类的方法。

在自定义类加载器时,需要重写该方法并编写加载规则,取得要加载类的字节码后转换成流,然后调用defineClass()方法生成类的 Class 对象。

defineClass(byte[] b, int off, int len)

将 byte 字节流解析成 JVM 能够识别的 Class 对象。

resolveClass(Class≺?≻ c)

解析 Class 对象,即将字节码文件中的符号引用转换为直接引用。

符号引用与直接引用

符号引用:即一个字符串,但是这个字符串给出了一些能够唯一性识别一个方法,一个变量,一个类的相关信息。

直接引用:可以理解为一个内存地址,或者一个偏移量。

举个例子,现在调用方法 hello(),这个方法的地址是 1234567 ,那么 hello 就是符号引用,1234567 就是直接引用。

类加载过程

类加载分为三个步骤:加载,连接,初始化

加载

根据一个类的全限定名(如 java.lang.String )来读取该类的二进制字节流,解析成 JVM 能够识别的 Class 对象。

连接

验证

确保 Class 文件的字节流中包含信息符合虚拟机要求,不会危害虚拟机的安全。

主要包括四种验证:文件格式验证,元数据验证,字节码验证,符号引用验证。

准备

为类的静态变量分配内存并且设置初始值,这里的初始值指的是不同类型的默认值,如 int 默认值为0,引用的默认值为 null。

而 final 修饰的静态常量,因为 final 在编译的时候就会分配了,所以此时的值为代码中设置的值。

注意

类的静态变量会分配在方法区中,而实例变量是随着对象一起分配到 Java 堆中。

解析

将常量池内的符号引用替换为直接引用。

初始化

将静态变量和静态方法块按顺序从上到下初始化,即为准备阶段的静态变量重新赋值,设置为代码中指定的值。

执行构造函数。

如果该类具有父类,先初始化父类。

流程图

子类继承父类时的执行顺序

更多相关文章

  1. MultiDex源码分析
  2. Android解决加载大图片时内存溢出的问题
  3. Android之Volley框架
  4. Android(安卓)游戏框架 libgdx 学习笔记 - 简介
  5. 【Android】ListView RecyclerView
  6. 第五章 Android加载PDF文件的使用
  7. android 在你的UI中显示Bitmap - 开发文档翻译
  8. Android编程 系统资源的介绍
  9. android-image-slide-panel图片照片墙的加载和滑动特效

随机推荐

  1. Android(安卓)Studio & HTTP Proxy
  2. android点击实现图片放大缩小
  3. XE7 Android(安卓)中使用 MessageDlg 范
  4. Android(安卓)-- MVP,MVC,MVVM
  5. Android(安卓)Ticks: display text verti
  6. Android中倒计时代码
  7. android 游戏OpenGL学习笔记1
  8. A first hand look at building an Andro
  9. Android调用第三方程序
  10. singleLine 过时