您的位置:首页 > 房产 > 建筑 > 南昌市经济技术开发区属于哪个区_黄山旅游攻略二日_整站优化seo平台_seo优化网站源码

南昌市经济技术开发区属于哪个区_黄山旅游攻略二日_整站优化seo平台_seo优化网站源码

2025/4/27 7:59:39 来源:https://blog.csdn.net/goTsHgo/article/details/147454980  浏览:    关键词:南昌市经济技术开发区属于哪个区_黄山旅游攻略二日_整站优化seo平台_seo优化网站源码
南昌市经济技术开发区属于哪个区_黄山旅游攻略二日_整站优化seo平台_seo优化网站源码

        我将以一个典型 Java 程序(HelloWorld)的执行过程为基础,逐步分析类加载的每一步,明确涉及的类、方法、输入数据格式、中间数据处理流程,以及最终输出数据格式。

        本文从程序启动开始,涵盖类加载的所有阶段(加载、链接、初始化),并具体到每个阶段调用的类和方法。


1. 概述:类加载的背景和流程

        类加载是 Java 虚拟机(JVM)将类文件(.class 文件或字节码)加载到内存,并将其转换为可执行的 java.lang.Class 对象的过程。它是 Java 程序运行的基础,确保程序所需的类在需要时被正确加载、验证和初始化。

类加载的三大阶段

  1. 加载(Loading):读取类文件的字节码到内存,生成 Class 对象。
  2. 链接(Linking)
    • 验证(Verification):检查字节码的合法性。
    • 准备(Preparation):为静态变量分配内存并设置默认值。
    • 解析(Resolution):将符号引用转换为直接引用。
  3. 初始化(Initialization):执行类的静态初始化代码(如静态块和静态变量赋值)。

类比:图书馆借书

  • 加载:从书库找到一本书(类文件)并放到借阅桌上(内存)。
  • 验证:检查书是否完整、合法(比如没有缺页)。
  • 准备:为书的笔记页(静态变量)分配空白空间。
  • 解析:把书中的参考文献(符号引用)替换为实际地址。
  • 初始化:填写书的笔记页(执行静态代码)。

示例程序

假设我们运行一个简单的 Java 程序 Main.java

public class Main {static int staticVar = 42;static {System.out.println("主程序中的静态块");}public static void main(String[] args) {System.out.println("Hello, World!");StaticTest test= new StaticTest();}
}class StaticTest {static {System.out.println("在StaticTest类中的静态块");}
}

我们将以这个程序的执行过程,详细描述类加载的完整流程。


2. 完整类加载流程

以下是类加载的详细步骤,涵盖从程序启动到类加载完成的每一步,明确涉及的类、方法、输入输出数据格式,以及中间处理流程。

2.1 程序启动:JVM 初始化

  • 触发点:运行 java Main 命令。
  • 涉及类
    • JVM 内部实现(非 Java 类,由 C++ 实现)。
    • java.lang.BootClassLoader(引导类加载器,JVM 内部)。
  • 操作
    • JVM 启动,初始化引导类加载器(BootClassLoader)。
    • BootClassLoader 加载核心类库(如 java.lang.Objectjava.lang.Stringjava.lang.System)。
    • JVM 调用 java.lang.System 的 initializeSystemClass() 方法,设置系统属性和环境。
  • 输入数据格式
    • 命令行参数:java Main
    • 核心类库路径(JAVA_HOME/lib 或模块系统中的 java.base)。
  • 中间处理
    • JVM 解析命令行,找到主类 Main
    • BootClassLoader 读取核心类文件的字节码(.class 文件或模块化镜像)。
    • 核心类的字节码被加载到 JVM 的方法区(Method Area),生成 java.lang.Class 对象。
  • 输出数据格式
    • 核心类的 Class 对象,存储在方法区。
    • JVM 环境初始化完成,准备加载用户类。

类比:图书馆开门,主管理员(JVM)先把核心书籍(如字典、工具书)放到桌上(内存),准备好借阅系统。


2.2 加载主类:Main

  • 触发点:JVM 需要执行 Main 类的 main 方法。
  • 涉及类
    • java.lang.AppClassLoader(应用类加载器)。
    • java.lang.ClassLoader(抽象类,提供加载逻辑)。
    • jdk.internal.loader.BuiltinClassLoaderAppClassLoader 的父类)。
    • jdk.internal.loader.URLClassPath(辅助类,管理类路径)。
  • 操作
    1. 查找主类
      • JVM 通过 AppClassLoader 查找 Main 类。
      • AppClassLoader 调用父类 ClassLoader 的 loadClass(String name) 方法。
      • loadClass 实现双亲委派模型:
        • 先调用 getParent() 获取父加载器(PlatformClassLoader)。
        • PlatformClassLoader 再委托其父加载器(BootClassLoader)。
        • 如果父加载器找不到类,AppClassLoader 调用自己的 findClass(String name) 方法。
    2. 读取字节码
      • AppClassLoader 通过 BuiltinClassLoader 的 findClassInModuleOrClassPath 方法查找 Main.class
      • URLClassPath 从类路径(-cp 或默认路径)读取 Main.class 的字节码。
    3. 创建 Class 对象
      • AppClassLoader 调用 ClassLoader 的 defineClass(String name, byte[] b, int off, int len) 方法。
      • JVM 将字节码转换为 java.lang.Class 对象,存储在方法区。
  • 输入数据格式
    • 类名:Main(全限定名 Main)。
    • 类路径:文件系统路径或 JAR 文件。
    • 字节码:Main.class 文件的二进制数据(字节数组)。
  • 中间处理
    • 双亲委派:确保类只加载一次,避免冲突。
      • BootClassLoader 检查是否为核心类(不是,失败)。
      • PlatformClassLoader 检查是否为平台类(不是,失败)。
      • AppClassLoader 从类路径找到 Main.class
    • 字节码读取URLClassPath 打开文件流,读取 Main.class 的二进制数据。
    • Class 对象生成:JVM 解析字节码,创建 Class 实例,记录类的元信息(如方法表、字段表)。
  • 输出数据格式
    • java.lang.Class 对象,表示 Main 类。
    • 存储在方法区,包含类的元信息(方法、字段、常量池等)。

类比:管理员(AppClassLoader)在图书馆(类路径)找到 Main 这本书(.class 文件),检查是否已在核心书库或分馆(父加载器),然后把书的内容(字节码)整理成一本可用的书(Class 对象)。


2.3 链接主类:Main

链接阶段将 Main 类的 Class 对象准备好,分为验证、准备和解析三个子阶段。

2.3.1 验证(Verification)
  • 触发点Main 类加载后,JVM 自动验证字节码。
  • 涉及类
    • JVM 内部验证器(非 Java 类)。
    • java.lang.ClassLoader(提供上下文)。
  • 操作
    • JVM 调用内部验证器,检查 Main 类的字节码是否合法。
    • 验证内容:
      • 文件格式:是否符合 JVM 规范(魔数 CAFEBABE、版本号等)。
      • 语义:方法和字段的访问权限是否合法。
      • 字节码:指令序列是否安全(无非法跳转)。
      • 符号引用:常量池中的引用是否有效。
  • 输入数据格式
    • Main 类的字节码(存储在方法区)。
  • 中间处理
    • JVM 解析字节码的常量池、方法表、字段表。
    • 检查字节码的结构和语义,确保不会导致 JVM 崩溃。
    • 如果验证失败,抛出 java.lang.VerifyError
  • 输出数据格式
    • 验证通过的 Class 对象,标记为“可继续链接”。

类比:管理员检查 Main 这本书是否完整(格式正确)、内容合法(没有危险指令),确保它可以安全借阅。

2.3.2 准备(Preparation)
  • 触发点:验证通过后,JVM 准备静态变量。
  • 涉及类
    • JVM 内部实现。
    • java.lang.Class(存储静态变量信息)。
  • 操作
    • JVM 为 Main 类的静态变量分配内存,并设置默认值。
    • 示例:static int staticVar = 42; 的 staticVar 被分配内存,默认值为 0(int 的默认值)。
  • 输入数据格式
    • Main 类的 Class 对象,包含静态变量的元信息。
  • 中间处理
    • JVM 在方法区为 staticVar 分配 4 字节(int 类型)。
    • 设置初始值 0,暂不执行赋值语句(42 在初始化阶段赋值)。
  • 输出数据格式
    • Class 对象,静态变量内存分配完成,默认值设置。

类比:管理员为 Main 书的笔记页(静态变量)分配空白空间,暂时填上默认内容(0)。

2.3.3 解析(Resolution)
  • 触发点:JVM 解析 Main 类的符号引用(可选,延迟解析可能在初始化或运行时进行)。
  • 涉及类
    • java.lang.ClassLoader(解析引用时可能调用父加载器)。
    • java.lang.Class(存储常量池)。
  • 操作
    • JVM 解析 Main 类常量池中的符号引用,转换为直接引用。
    • 示例:Main 类的 main 方法调用 System.out.println,涉及 java.lang.System 和 java.io.PrintStream
    • JVM 通过 AppClassLoader 加载 System 和 PrintStream 类(如果尚未加载)。
  • 输入数据格式
    • Main 类的常量池,包含符号引用(如 CONSTANT_Class_info 指向 java.lang.System)。
  • 中间处理
    • JVM 查找符号引用的类(如 System),调用 ClassLoader.loadClass("java.lang.System")
    • BootClassLoader 加载 System 类,生成 Class 对象。
    • 常量池中的符号引用(如 #2)被替换为 Class 对象的内存地址。
  • 输出数据格式
    • Class 对象,常量池中的符号引用更新为直接引用。

类比:管理员把 Main 书中的参考书目(符号引用)替换为具体书的地址(直接引用),确保可以快速找到其他书。


2.4 初始化主类:Main

  • 触发点:JVM 准备执行 Main.main 方法前,初始化 Main 类。
  • 涉及类
    • java.lang.Class(存储静态代码)。
    • JVM 内部实现(执行初始化)。
  • 操作
    • JVM 执行 Main 类的静态初始化代码:
      • 静态变量赋值:staticVar = 42;
      • 静态块:System.out.println("主程序中的静态块");
    • JVM 调用 <clinit> 方法(类初始化方法,由编译器生成)。
  • 输入数据格式
    • Main 类的 Class 对象,包含 <clinit> 方法。
  • 中间处理
    • JVM 执行 <clinit> 方法:
      • 设置 staticVar = 42
      • 执行静态块,调用 System.out.println,输出 "主程序中的静态块"
    • 如果涉及其他类(如 System),可能触发它们的加载和初始化。
  • 输出数据格式
    • Main 类的静态变量初始化完成(staticVar = 42)。
    • 静态块执行,输出到控制台。
    • Class 对象标记为“已初始化”。

类比:管理员正式填写 Main 书的笔记页(静态变量和静态块),完成书的准备工作。


2.5 执行 main 方法:触发其他类加载

  • 触发点:JVM 调用 Main.main(String[] args)
  • 涉及类
    • java.lang.AppClassLoader(加载 StaticTest类)。
    • java.lang.ClassLoader(提供加载逻辑)。
    • jdk.internal.loader.BuiltinClassLoader
    • jdk.internal.loader.URLClassPath
    • java.lang.Class(表示 StaticTest类)。
  • 操作
    1. 执行 main 方法
      • 输出 "Hello, World!"(调用 System.out.println)。
      • 创建 StaticTest实例:new StaticTest()
    2. 加载 StaticTest
      • new StaticTest() 触发 StaticTest类的加载。
      • AppClassLoader 调用 loadClass("StaticTest")
      • 类似 Main 类,URLClassPath 读取 StaticTest.class 的字节码。
      • defineClass 创建 StaticTest的 Class 对象。
    3. 链接 StaticTest 类
      • 验证:检查 StaticTest字节码的合法性。
      • 准备:为 StaticTest的静态变量分配内存。
      • 解析:解析 StaticTest的符号引用(如 System)。
    4. 初始化 StaticTest
      • 执行 <clinit> 方法,运行静态块:System.out.println("在StaticTest类中的静态块");
    5. 创建 StaticTest实例
      • JVM 调用 StaticTest的构造方法,分配实例内存,生成对象。
  • 输入数据格式
    • 类名:StaticTest
    • 字节码:StaticTest.class 文件的二进制数据。
  • 中间处理
    • 加载AppClassLoader 通过双亲委派查找 StaticTest,从类路径加载字节码。
    • 链接:验证字节码,分配静态变量内存,解析引用。
    • 初始化:执行静态块,输出 "在StaticTest类中的静态块"
    • 实例化:分配堆内存,调用构造方法。
  • Output data format:
    • StaticTest类的 Class 对象(方法区)。
    • StaticTest实例(堆内存)。
    • 控制台输出:"在StaticTest类中的静态块"

类比:程序运行时,管理员发现需要 StaticTest这本书,重复“找书 -> 检查 -> 准备 -> 初始化”的过程,最终借出书并创建一本新副本(实例)。


2.6 程序输出

运行 java Main 的完整输出:

主程序中的静态块
Hello, World!
在StaticTest类中的静态块
  • 解释
    • Main 类的静态块在初始化时执行。
    • main 方法输出 "Hello, World!"
    • StaticTest类的静态块在 new StaticTest() 时执行。

3. 涉及的类和方法总结

以下是类加载流程中涉及的所有类和关键方法:

阶段方法作用
启动JVM 内部实现, BootClassLoader(无 Java 方法,由 JVM 调用)初始化 JVM,加载核心类库(如 java.lang.System)。
加载AppClassLoaderClassLoaderBuiltinClassLoaderURLClassPathloadClass(String)findClass(String)defineClass(String, byte[], int, int)URLClassPath.getResource()查找并读取 .class 文件,生成 Class 对象。
验证JVM 内部, ClassLoader(无直接 Java 方法,JVM 验证器)检查字节码合法性。
准备JVM 内部, Class(无直接 Java 方法,JVM 分配内存)为静态变量分配内存,设置默认值。
解析ClassLoaderClassloadClass(String)(间接调用)将符号引用转换为直接引用。
初始化Class, JVM 内部<clinit>(由 JVM 执行)执行静态变量赋值和静态块。
运行AppClassLoaderClassLoaderClassloadClass(String)defineClass<init>(构造方法)加载依赖类(如 StaticTest),创建实例。

4. 数据格式总结

阶段输入数据格式中间处理输出数据格式
加载类名(String),.class 文件(字节数组)双亲委派查找,读取字节码,解析元信息,生成 Class 对象java.lang.Class 对象(方法区)
验证字节码(Class 对象的元信息)检查文件格式、语义、字节码指令、符号引用验证通过的 Class 对象
准备Class 对象(静态变量元信息)分配内存,设置默认值(如 int 为 0Class 对象(静态变量内存分配完成)
解析常量池(符号引用)加载依赖类,替换符号引用为直接引用(内存地址)Class 对象(常量池更新)
初始化Class 对象(<clinit> 方法)执行静态变量赋值和静态块,可能触发其他类加载Class 对象(静态变量初始化完成),控制台输出
运行类名,字节码,构造参数加载依赖类,执行构造方法,分配堆内存Class 对象,实例对象(堆),控制台输出

5. 补充说明

5.1 双亲委派模型

  • 确保类加载的唯一性和安全性。
  • 流程:子加载器(如 AppClassLoader)先委托父加载器(如 PlatformClassLoaderBootClassLoader),只有父加载器失败时才自己加载。
  • 涉及方法:ClassLoader.loadClass 和 findClass

5.2 模块化支持(Java 9+)

  • PlatformClassLoader 和 AppClassLoader 支持模块系统。
  • java.lang.Module 定义模块边界,影响类加载的可见性。
  • 示例中,Main 和 StaticTest可能属于同一模块(如 unnamed module)。

6. 总结

类加载的完整流程从 JVM 启动到程序运行,涉及以下关键步骤:

  1. 启动:JVM 初始化,BootClassLoader 加载核心类。
  2. 加载AppClassLoader 通过 loadClass 和 defineClass 加载 Main 类,生成 Class 对象。
  3. 链接
    • 验证:JVM 检查字节码合法性。
    • 准备:分配静态变量内存,设置默认值。
    • 解析:将符号引用替换为直接引用。
  4. 初始化:执行 Main 的 <clinit> 方法,初始化静态变量和块。
  5. 运行:执行 main 方法,触发 StaticTest类的加载、链接、初始化和实例化。

涉及的核心类ClassLoaderAppClassLoaderPlatformClassLoaderBootClassLoaderBuiltinClassLoaderURLClassPathClassModule

数据流:从类名和字节码文件开始,经过加载、验证、准备、解析、初始化,最终生成 Class 对象和实例,输出到控制台。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com