前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >面试官:请你谈谈 Java 的类加载过程

面试官:请你谈谈 Java 的类加载过程

作者头像
CG国斌
修改2020-05-14 14:27:05
2.1K0
修改2020-05-14 14:27:05
举报
文章被收录于专栏:维C果糖维C果糖

刚刚走出校门的应届毕业生,如果在去寻求一份 Java 开发的工作时,你的面试官很有可能一边看着你的简历,一边漫不经心地问你:了解过 Java 类的加载过程吗?

这个时候你一定要注意了,虽然这是一个老生常谈的问题,但是这也是一个非常能够考验你 Java 功底的问题。

如果你答好了,这是你应该的;如果你没答好,那么对不起,面试官心中已经给了你不及格。

今天,小编就 Java 类加载过程这个问题,抛砖引玉,说一下自己的理解,如果有不对的地方,欢迎大家在评论去指正。

前言

一个 Java 文件从编码完成到最终执行,一般主要包括两个过程

  • 编译
  • 运行

编译,即把我们写好的 Java 文件,通过javac命令编译成字节码,也就是我们常说的.class文件。

运行,则是把编译生成的.class文件交给 Java 虚拟机( JVM )执行。

而我们所说的类加载过程即是指 JVM 虚拟机把.class文件中类信息加载进内存,并进行解析生成对应的class对象的过程。

举个通俗点的例子来说,JVM 在执行某段代码时,遇到了class A, 然而此时内存中并没有class A的相关信息,于是 JVM 就会到相应的class文件中去寻找class A的类信息,并加载进内存中,这就是我们所说的类加载过程。

由此可见,JVM 不是一开始就把所有的类都加载进内存中,而是只有第一次遇到某个需要运行的类时才会加载,且只加载一次。

类加载

类加载的过程主要分为三个部分:

  • 加载
  • 链接
  • 初始化

而链接又可以细分为三个小部分:

  • 验证
  • 准备
  • 解析
class-load
class-load

加载

简单来说,加载指的是把class字节码文件从各个来源通过类加载器装载入内存中。这里有两个重点:

  • 字节码来源。一般的加载来源包括从本地路径下编译生成的.class文件,从jar包中的.class文件,从远程网络,以及动态代理实时编译。
  • 类加载器。一般包括启动类加载器,扩展类加载器,应用类加载器以及用户的自定义类加载器。

注:为什么会有自定义类加载器?

  • 一方面是由于 Java 代码很容易被反编译,如果需要对自己的代码加密的话,可以对编译后的代码进行加密,然后再通过实现自己的自定义类加载器进行解密,最后再加载。
  • 另一方面也有可能从非标准的来源加载代码,比如从网络来源,那就需要自己实现一个类加载器,从指定源进行加载。

链接

验证

主要是为了保证加载进来的字节流符合虚拟机规范,不会造成安全错误。

包括对于文件格式的验证,比如常量中是否有不被支持的常量?文件中是否有不规范的或者附加的其他信息?

对于元数据的验证,比如该类是否继承了被final修饰的类?类中的字段,方法是否与父类冲突?是否出现了不合理的重载?

对于字节码的验证,保证程序语义的合理性,比如要保证类型转换的合理性。

对于符号引用的验证,比如校验符号引用中通过全限定名是否能够找到对应的类?校验符号引用中的访问性(privatepublic等)是否可被当前类访问?

准备

主要是为类变量(注意,不是实例变量)分配内存,并且赋予初值。

特别需要注意,初值,不是代码中具体写的初始化的值,而是 Java 虚拟机根据不同变量类型的默认初始值。

比如 8 种基本类型的初值,默认为 0;引用类型的初值则为null;常量的初值即为代码中设置的值,例如final static tmp = 456, 那么该阶段 456 就是tmp的初值。

解析

将常量池内的符号引用替换为直接引用的过程。两个重点:

  • 符号引用。即一个字符串,但是这个字符串给出了一些能够唯一性识别一个方法,一个变量,一个类的相关信息。
  • 直接引用。可以理解为一个内存地址,或者一个偏移量。比如类方法,类变量的直接引用是指向方法区的指针;而实例方法,实例变量的直接引用则是从实例的头指针开始算起到这个实例变量位置的偏移量。

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

在解析阶段,虚拟机会把所有的类名,方法名,字段名这些符号引用替换为具体的内存地址或偏移量,也就是直接引用。

初始化

这个阶段主要是对类变量初始化,是执行类构造器的过程。

换句话说,只对static修饰的变量或语句进行初始化。

如果初始化一个类的时候,其父类尚未初始化,则优先初始化其父类。

如果同时包含多个静态变量和静态代码块,则按照自上而下的顺序依次执行。

总结

类加载过程只是一个类生命周期的一部分,在其前,有编译的过程,只有对源代码编译之后,才能获得能够被虚拟机加载的字节码文件;在其后,还有具体的类使用过程,当使用完成之后,还会在方法区垃圾回收的过程中进行卸载。如果想要了解 Java 类整个生命周期的话,可以自行上网查阅相关资料,这里不再多做赘述。

在面试过程中类加载过程虽然是一个老生常谈的问题,但是往往从这个问题还可以衍生出很多其他重要的知识点,已经罗列在下文中,如果大家感兴趣的话,可以自行学习,小编也会在之后的文章中,对其中的一些问题进行解答和总结。

相关扩展知识点:

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 类加载
    • 加载
      • 链接
        • 验证
        • 准备
        • 解析
      • 初始化
      • 总结
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档