文章目录
- 类加载过程的三个阶段
- 一个简单的案例:类加载的工作原理
- 使用这个类
- 类加载的顺序
- 类加载的特点
- 类加载的好处
- 总结
- 推荐阅读文章
在 Java 中, 类加载是一种将我们写的 Java 类文件加载到内存中的过程,让 JVM(Java 虚拟机)可以识别和执行它们。Java 类加载是一个神奇的过程,因为它让我们写的代码从“静态”的文件,变成了“动态”可执行的程序。
要理解类加载,我们可以从三个主要阶段来了解它们的作用:加载、连接 和 初始化。
类加载过程的三个阶段
-
加载(Loading):
- 在这个阶段,JVM 通过类加载器(ClassLoader)找到我们写的
.class
文件,把它们的字节码读进内存。 - 这个过程可以想象成打开一本书,把它从书架上取出来,放到桌子上开始阅读。
- 在这个阶段,JVM 通过类加载器(ClassLoader)找到我们写的
-
连接(Linking):
- 连接过程有三个小步骤:验证、准备 和 解析。
- 验证:JVM 检查类文件的格式是否正确,确保没有错误的字节码。
- 准备:JVM 为类的变量分配内存空间,并初始化默认值。
- 解析:JVM 把类中的符号引用(如方法名、变量名)转换成直接引用,让它们可以被实际调用。
- 类似于读书时理解内容的逻辑,比如检查章节标题和内容是不是一致。
- 连接过程有三个小步骤:验证、准备 和 解析。
-
初始化(Initialization):
- 这是类加载的最后一步。JVM 会执行类中的静态初始化代码和静态变量的赋值操作。
- 可以理解为在正式读书前标注关键内容,为阅读做准备。
一个简单的案例:类加载的工作原理
假设我们有一个简单的类 HelloWorld
:
public class HelloWorld {static String message = "Hello, Java!";static {System.out.println("HelloWorld 类已被加载!");}public void sayHello() {System.out.println(message);}
}
这个 HelloWorld
类有一个静态变量 message
和一个静态代码块。静态代码块中的 System.out.println()
会在类加载时执行。
使用这个类
我们编写一个 Main
类来使用 HelloWorld
:
public class Main {public static void main(String[] args) {HelloWorld hello = new HelloWorld(); // 第一次使用 HelloWorld 类hello.sayHello(); // 调用方法}
}
当我们运行 Main
类时,会看到以下输出:
HelloWorld 类已被加载!
Hello, Java!
类加载的顺序
让我们分解一下这个过程发生了什么:
-
加载:
- 当
Main
类中第一次创建HelloWorld
对象时,JVM 发现HelloWorld
类还没有被加载。 - 类加载器加载
HelloWorld.class
文件,将其加载进内存。
- 当
-
连接:
- JVM 连接
HelloWorld
类,检查它的格式(验证)、为message
分配内存并设置默认值(准备),然后解析类中的方法和变量引用。
- JVM 连接
-
初始化:
- JVM 执行
HelloWorld
类的静态代码块System.out.println("HelloWorld 类已被加载!");
,这就是我们看到的第一行输出。 - 同时,把
message
静态变量赋值为"Hello, Java!"
。
- JVM 执行
类加载的特点
-
延迟加载(Lazy Loading):
- Java 类的加载是延迟的,JVM 在真正使用一个类时才会加载它。这就是为什么
HelloWorld
类的静态代码块在我们调用new HelloWorld()
时才会执行。
- Java 类的加载是延迟的,JVM 在真正使用一个类时才会加载它。这就是为什么
-
类加载器(ClassLoader):
- Java 中有三种类加载器,分别是启动类加载器、扩展类加载器和应用类加载器。
- 启动类加载器负责加载核心 Java 类库,比如
java.lang.String
。 - 扩展类加载器负责加载一些扩展类。
- 应用类加载器负责加载应用程序中定义的类,比如我们的
HelloWorld
类。
- 启动类加载器负责加载核心 Java 类库,比如
- Java 中有三种类加载器,分别是启动类加载器、扩展类加载器和应用类加载器。
-
单一实例:
- 一个类在整个应用中只会被加载一次。如果我们多次创建
HelloWorld
对象,静态代码块不会重复执行,这就是类加载的“单一实例”特性。
- 一个类在整个应用中只会被加载一次。如果我们多次创建
类加载的好处
类加载过程是 Java 的一个核心优势:
- 安全性:加载过程中的验证步骤帮助确保类文件不会有错误的字节码,保护程序不被恶意代码破坏。
- 效率:Java 只在需要时加载类,减少了内存占用。
- 可扩展性:类加载器机制允许动态加载类,比如在 Java Web 应用中可以动态加载第三方库。
总结
Java 类加载是一个将 .class
文件加载到内存的过程,通过加载、连接和初始化的三个步骤来确保类能够被正确地使用。类加载机制保证了代码的安全性、可维护性和内存管理的效率。理解类加载,可以帮助我们更好地使用 Java 的类和对象,让代码运行得更加流畅!
希望这个解释让你对 Java 类加载有了更清晰的认识!
推荐阅读文章
-
由 Spring 静态注入引发的一个线上T0级别事故(真的以后得避坑)
-
如何理解 HTTP 是无状态的,以及它与 Cookie 和 Session 之间的联系
-
HTTP、HTTPS、Cookie 和 Session 之间的关系
-
什么是 Cookie?简单介绍与使用方法
-
什么是 Session?如何应用?
-
使用 Spring 框架构建 MVC 应用程序:初学者教程
-
有缺陷的 Java 代码:Java 开发人员最常犯的 10 大错误
-
如何理解应用 Java 多线程与并发编程?
-
把握Java泛型的艺术:协变、逆变与不可变性一网打尽
-
Java Spring 中常用的 @PostConstruct 注解使用总结
-
如何理解线程安全这个概念?
-
理解 Java 桥接方法
-
Spring 整合嵌入式 Tomcat 容器
-
Tomcat 如何加载 SpringMVC 组件
-
“在什么情况下类需要实现 Serializable,什么情况下又不需要(一)?”
-
“避免序列化灾难:掌握实现 Serializable 的真相!(二)”
-
如何自定义一个自己的 Spring Boot Starter 组件(从入门到实践)
-
解密 Redis:如何通过 IO 多路复用征服高并发挑战!
-
线程 vs 虚拟线程:深入理解及区别
-
深度解读 JDK 8、JDK 11、JDK 17 和 JDK 21 的区别
-
10大程序员提升代码优雅度的必杀技,瞬间让你成为团队宠儿!
-
“打破重复代码的魔咒:使用 Function 接口在 Java 8 中实现优雅重构!”
-
Java 中消除 If-else 技巧总结
-
线程池的核心参数配置(仅供参考)
-
【人工智能】聊聊Transformer,深度学习的一股清流(13)
-
Java 枚举的几个常用技巧,你可以试着用用
-
由 Spring 静态注入引发的一个线上T0级别事故(真的以后得避坑)
-
如何理解 HTTP 是无状态的,以及它与 Cookie 和 Session 之间的联系
-
HTTP、HTTPS、Cookie 和 Session 之间的关系
-
使用 Spring 框架构建 MVC 应用程序:初学者教程
-
有缺陷的 Java 代码:Java 开发人员最常犯的 10 大错误
-
Java Spring 中常用的 @PostConstruct 注解使用总结
-
线程 vs 虚拟线程:深入理解及区别
-
深度解读 JDK 8、JDK 11、JDK 17 和 JDK 21 的区别
-
10大程序员提升代码优雅度的必杀技,瞬间让你成为团队宠儿!
-
探索 Lombok 的 @Builder 和 @SuperBuilder:避坑指南(一)
-
为什么用了 @Builder 反而报错?深入理解 Lombok 的“暗坑”与解决方案(二)