Java类加载机制,这篇大概、也许、可能就够了

简介: 写在前面关于Java类加载机制一至有没办法说的痛苦。因为当初我在学习这方面的内容时,多多少少有一些懵逼,所以这次的文章,将尽可能的把概念性的东西转化成容易理解的内容,所以希望各位看到文章的童鞋可以有所收获~正文开始第一步,先让咱们看一段代码:public class Main { static{ System.

写在前面

关于Java类加载机制一至有没办法说的痛苦。因为当初我在学习这方面的内容时,多多少少有一些懵逼,所以这次的文章,将尽可能的把概念性的东西转化成容易理解的内容,所以希望各位看到文章的童鞋可以有所收获~

正文开始

第一步,先让咱们看一段代码:


public class Main {
    static{
        System.out.println("我是静态代码块");
    }
    
    {
        System.out.println("我是实例代码块");
    }
    
    public static void main(String[] args) {
        Main main1=new Main();
        Main main2=new Main();
    }
}

各位小伙伴,这段代码run起来之后会是什么样的结构?这里就不卖关子了,直接贴结果。

img_4a7f52a95ccddc433c5c45bf44cf6fdd.png
image

OK,如果小伙伴们,知道这个结果,并且也理解这个结果,那么接下来的内容就可以跳过啦。如果有疑问的话,那就让我们带着这个答案,往下看,内容很少,。重在理解~


Java类加载机制

先看一下概念

虚拟机把描述类的数据从class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制。(来源《深入理解Java虚拟机.第二版》以下简称为深入JVM)

概念总是枯燥的,让我们开始对这个概念进行一些便于理解的分析和梳理。

img_94115f497ad89c939a6100333f9416d8.png
image

梳理

1、加载

首先是加载阶段:此阶段是Java将字节码(.class文件)数据从不同的数据源(我们的jar文件、class 文件,甚至是网络数据源等;只要结构正确即可)读取到JVM中,并映射为JVM认可的数据结构(Class 对象,可以理解成就是java.lang.Class) 。
按照《深入JVM》的描述,这个过程有三步(有部分用词的加工):

  • 1、通过一个类的全限定名来获取定义此类的二进制字节流。(也就是先通过路径找到这个类的.class文件)
  • 2、将这个.class字节流所代表的的代码结构转化为方法区的运行时数据结构。(可以理解为此时已经在虚拟机中成了能够被识别的代码结构)
  • 3、在内存生成能够代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的入口。(这里很不好理解,我对此的理解是:虽然代码结构已经存在,但是我们没有办法直接去使用。因此这里抽象出了java.lang.Class对象作为方位.class文件字节码的接口。)

在这里,我曾经有些疑惑那就是字节码和二进制文件。其实都是.class文件,我们简单编译一下上述的Main.java

img_9d03b87546a09544a1e68c64496835fa.png
image

首先我们移动到Main所在的目录,编译并查看.class的字节码。

img_faea60d71aafb0e7a20ec8d599dc8c98.png
image

ok,这是字节码,如果我们使用一些文本编辑器,比如Sublime,我们看到就是二进制形式的文件内容:

img_04768319ae7c77516f10f1b307ae52a3.png
image

Tips:加载阶段,我们可以自定义类加载器,去实现自己的类加载过程。

2、连接

连接和加载过程是交叉进行的,也就是说加载阶段没有完成,连接阶段可能就已经开始了。

第二阶段是连接 ,这是核心的步骤,简单说是把原始的类定义信息平滑地转化入JVM运行的过程中。这里可进一步细分为三个步骤:

  • 1、验证 :这是虚拟机安全的重要保障,JVM 需要核验字节信息是符合Java虚拟机规范的,验证阶段有可能触发更多class的加载。
  • 2、准备:创建类或接口中的静态变量,并初始化静态变量的初始值。这里的初始化重点在于分配所需要的内存空间,不会进行赋值,也就是说这里初始化的值是默认值,比如public static int value = 666,此时的value等于0,而非666。而真正的赋值操作在初始化阶段。
  • 3、解析:在这一步会将常量池中的符号引用(symbolic reference)替换为直接引用。这段很短的文字我理解了很久,因为它包含了大量的概念:常量池、符号引用、直接引用。接下来逐条解释:

常量池:常量池里除了String对象,final类型的常量,还有符号引用
符号引用:用于描述字节码文件中各字段,各方法、各接口等。我是这么理解的:如果字段、方法都是想象想要旅游的游客的话,那么符号引用就是旅游公司,但是旅游公司只负责收钱组织游客,他们不负责真正带游客出去玩,真正带他们去玩的是导游(直接引用)。也就是说符号引用就是一个能够代表所有字段、方法的这么一个角色。
直接引用:直接引用想到于能够找到对应内存地址的角色,也就是上述例子中的导游。


PS:不知道这么解释能不能理解解析的过程,如果还是迷糊,可以查看知乎大佬对此的专业回答:https://www.zhihu.com/question/30300585

3、初始化

最后是初始化阶段 , 这一步开始执行静态字段赋值的动作,静态初始化块内的逻辑,编译器在编译阶段就已经把该执行的代码逻辑整理好了,这里需要注意的是:父类的初始化逻辑优先于子类的逻辑。

这里有一个细节需要注意:静态代码块中只能访问到定义在静态代码块之前的变量。如果此静态变量在静态代码块后边,那么静态代码块里只能对其赋值,不可访问:

img_12613162aa62279a044a19c78e26e5d0.png
image

加载结束

一直走到这,我们的类正式加载完毕,也是生成了我们对应的Class对象。但是请留意,这里还没有涉及到类的实例化,也就是说此时还没有开始new操作。

当执行new的时候,而且类已经经历过加载,那么才会执行对应的实例化,比如分配内存,执行代码块,构造方法之类的(如果有父类要先对应执行这些内容)。

触发类加载的操作

  • new关键字;get/set一个static变量(final、在编译期进入常量池的静态字段除外);调用static方法。
  • 使用反射,如果此类没有被加载会先进行加载操作。
  • main方法对应的类,会在JVM启动是加载。
  • 使用一些动态代理方式时。

双亲委派机制

关于双亲委派机制,MDove的文章说的简单明了,其实就是一张图:

img_76a96c73d28fa8f83fc6d5f3f5c5e88c.png
image

用《深入JVM》的话,解释一下:
双亲委派模型的工作过程:如果一个类加载器收到了一个类的加载请求时,首先它不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层级的类加载器都是如此。因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求时,子加载器才会尝试自己去加载。

尾声

关于类加载的的梳理,到此就结束了,不知道各位小伙伴们有没有理解呢?如果小伙伴们有自己的理解,或者文中有不当之处,欢迎评论区留言~此致敬礼!

我是一个应届生,最近和朋友们维护了一个公众号,内容是我们在从应届生过渡到开发这一路所踩过的坑,已经我们一步步学习的记录,如果感兴趣的朋友可以关注一下,一同加油~

img_89788b3a8f3f86257453cbc8264959f6.png
个人公众号:IT面试填坑小分队
目录
相关文章
|
6月前
|
安全 Java 程序员
学习Java类加载机制
在Java的世界里,每一个类或者接口,在经历编译器后,都会生成一个个.class文件。
40 0
|
6月前
|
Java
一个表格带你搞懂java的类加载机制
一个表格带你搞懂java的类加载机制,加载->验证->准备->解析->初始化->使用.卸载
|
SQL 前端开发 Java
大数据平台底层技术-JAVA篇-如何动态加载不同版本的 HIVE JDBC 驱动 - 一文读懂JAVA的类加载机制 1
大数据平台底层技术-JAVA篇-如何动态加载不同版本的 HIVE JDBC 驱动 - 一文读懂JAVA的类加载机制
|
3月前
|
存储 Java 数据库连接
Java类文件结构及类加载机制
该文章主要讨论了Java类文件的结构以及Java类的加载机制,并提到了双亲委派模型的相关内容。
Java类文件结构及类加载机制
|
3月前
|
数据库 C# 开发者
WPF开发者必读:揭秘ADO.NET与Entity Framework数据库交互秘籍,轻松实现企业级应用!
【8月更文挑战第31天】在现代软件开发中,WPF 与数据库的交互对于构建企业级应用至关重要。本文介绍了如何利用 ADO.NET 和 Entity Framework 在 WPF 应用中访问和操作数据库。ADO.NET 是 .NET Framework 中用于访问各类数据库(如 SQL Server、MySQL 等)的类库;Entity Framework 则是一种 ORM 框架,支持面向对象的数据操作。文章通过示例展示了如何在 WPF 应用中集成这两种技术,提高开发效率。
52 0
|
3月前
|
安全 前端开发 Java
【JVM 探秘】ClassLoader 类加载器:揭秘 Java 类加载机制背后的秘密武器!
【8月更文挑战第25天】本文全面介绍了Java虚拟机(JVM)中的类加载器,它是JVM的核心组件之一,负责将Java类加载到运行环境中。文章首先概述了类加载器的基本工作原理及其遵循的双亲委派模型,确保了核心类库的安全与稳定。接着详细阐述了启动、扩展和应用三种主要类加载器的层次结构。并通过一个自定义类加载器的例子展示了如何从特定目录加载类。此外,还介绍了类加载器的完整生命周期,包括加载、链接和初始化三个阶段。最后强调了类加载器在版本隔离、安全性和灵活性方面的重要作用。深入理解类加载器对于掌握JVM内部机制至关重要。
144 0
|
4月前
|
Java 程序员 开发者
Java中的类加载机制详解
Java中的类加载机制详解
|
5月前
|
存储 前端开发 Java
深入解析Java类加载机制:原理、过程与实践
深入解析Java类加载机制:原理、过程与实践
196 2
|
4月前
|
前端开发 Java 数据安全/隐私保护
深入理解Java中的类加载机制
深入理解Java中的类加载机制
|
5月前
|
前端开发 Java 开发者
Java中的类加载机制与双亲委派模型
Java中的类加载机制与双亲委派模型
下一篇
无影云桌面