Java安全
Java 基础知识
01概述
02变量
03运算符
04程序控制语句
05数组
06面向对象编程
07代码练习
08重载
09作用域
10构造方法&构造器
11this
12包
13修饰符
14封装
15继承
16super
17覆盖&重写
18多态
19零钱通项目
20类变量&类方法
21抽象类
22接口
23内部类
24枚举
25泛型
26常用API
27lambda表达式
28正则表达式
29异常
30File&IO流
31日志技术
32多线程
33网络编程
01反射
02反序列化
03JVM
04JDBC
05RMI
06JRMP
07JNDI
08CDI
09JPA
10Servlet
11Filter
12MVC模型
13MVC框架
14类的加载机制
15Maven
16注解
17ORM
18CC链
19JNDI注入
Log4j2
-
+
首页
14类的加载机制
 Java 的类的加载机制是指将.class 字节码文件加载到 JVM(Java 虚拟机)中,并转化为可执行的运行时数据结构的过程。 类加载的过程主要分为三个阶段:加载、连接、初始化。 ## 加载阶段 加载阶段是类加载过程的第一个阶段。在这个阶段,虚拟机需要完成以下三个主要任务: 首先,通过类的全名获取定义此类的二进制字节流。这个二进制字节流可以从多种来源获取,比如本地文件系统、网络、数据库等。 其次,将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。方法区是 JVM 中用于存储类的元数据信息的区域,这个转化过程就是将字节流中的信息解析并存储到方法区中。 最后,在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。通过这个Class对象,我们可以获取类的各种信息,如字段、方法、构造函数等。加载阶段可以由用户自定义的类加载器去完成,也可以通过 Java 虚拟机自身去完成。 ## 连接阶段 连接阶段是类加载过程的第二个阶段,它又可以细分为验证、准备和解析三个子阶段: 1. 验证阶段:验证阶段是为了确保被加载的类的正确性。它包括文件格式验证,检查字节流是否符合 Class 文件的格式规范;元数据验证,检查类的元数据信息是否符合 Java 语言的规范;字节码验证,检查字节码指令的语义和逻辑是否正确;符号引用验证,确保类对其他类、字段、方法等的引用是正确的。 2. 准备阶段:准备阶段是正式为类变量分配内存并设置类变量初始值的阶段。这些变量所使用的内存都将在方法区中进行分配。需要注意的是,这里设置的初始值是数据类型的默认值,比如int类型的默认值是 0,boolean类型的默认值是false等。 3. 解析阶段:解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程。符号引用是一种间接的引用,它以字符串的形式表示对其他类、字段、方法等的引用;而直接引用是一种直接的内存地址或句柄,它可以直接指向目标对象。解析阶段的目的是将这些间接引用转化为直接引用,以便在运行时能够更快地访问目标对象。 ## 初始化阶段 初始化阶段是类加载过程的最后一个阶段。在这个阶段,虚拟机执行类构造器\<clinit>()方法,此方法是由编译器自动收集类中的所有类变量的赋值动作和静态代码块(static{}块)中的语句合并产生的。当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化。虚拟机会保证一个类的\<clinit>()方法在多线程环境中被正确地加锁和同步,以避免多个线程同时初始化同一个类。 类加载的这三个阶段在 Java 反射机制中起着至关重要的作用。反射机制允许程序在运行时动态地加载、链接和使用类,从而提高了程序的灵活性和可扩展性。但这也带来了额外的性能开销,因为类加载和反射操作通常比直接调用类的方法要慢得多。 在 Java 应用中,类加载器是实现反射机制的关键组件之一。类加载器负责根据类的全名来加载对应的类,并为其创建Class对象。Java 提供了三种默认的类加载器:引导类加载器(Bootstrap ClassLoader)、扩展类加载器(Extension ClassLoader)和系统类加载器(System ClassLoader)。这些类加载器之间形成了父子关系,当需要加载一个类时,会首先尝试由父类加载器去加载,如果父类加载器无法找到该类,则会由子类加载器去尝试加载。 ### 类加载器(ClassLoader) 类加载的 “加载” 阶段由类加载器负责,它是实现类加载机制的核心组件。JVM 提供了一套层级化的类加载器,同时允许用户自定义类加载器。 ### 核心类加载器 JVM 默认提供 3 种类加载器,按 “父子关系”(逻辑上的继承,非 Java 类的继承)分为: | 类加载器 | 作用范围 | 父加载器 | | ------------------------------------------- | ------------------------------------------------------------ | ---------------- | | 启动类加载器(Bootstrap ClassLoader) | 加载 JDK 核心类库(如rt.jar中的java.lang.、java.util.),由 C++ 实现(非 Java 类)。 | 无(顶层加载器) | | 扩展类加载器(Extension ClassLoader) | 加载 JRE 扩展目录中的类(如jre/lib/ext目录下的 JAR 包),由 Java 类sun.misc.Launcher$ExtClassLoader实现。 | 启动类加载器 | | 应用程序类加载器(Application ClassLoader) | 加载用户类路径(classpath)上的类(如项目中的com.example.User),由 Java 类sun.misc.Launcher$AppClassLoader实现。 | 扩展类加载器 | ### 双亲委派模型(Parent Delegation Model) JVM 类加载的核心机制,规定:当一个类加载器需要加载类时,先委托给其父加载器加载;若父加载器无法加载(找不到类),再由自己加载。 > 委派模型 例如加载com.example.User: 1. 应用程序类加载器 → 委托给扩展类加载器; 2. 扩展类加载器 → 委托给启动类加载器; 3. 启动类加载器无法加载(com.example.User不在核心类库),返回给扩展类加载器; 4. 扩展类加载器无法加载(不在ext目录),返回给应用程序类加载器; 5. 应用程序类加载器在classpath中找到并加载。 > 作用 防止类重复加载:同一类被不同加载器加载会视为不同类(JVM 判断类唯一性的标准:全限定名 + 类加载器),双亲委派保证一个类仅被一个加载器加载; 保证核心类安全:如java.lang.String只能被启动类加载器加载,防止用户自定义同名类(java.lang.String)替换核心类,避免恶意篡改。 ### 自定义加载器 当需要加载非classpath中的类(如加密的.class 文件、网络中的类)时,可通过继承java.lang.ClassLoader实现自定义类加载器,核心是重写findClass方法(而非loadClass,避免破坏双亲委派)。 ## 类初始化的触发时机 类的 “初始化” 阶段(执行\<clinit>())需要满足特定条件才会触发,JVM 规范规定以下场景会触发初始化(称为 “主动引用”): 1. 当使用new关键字创建类的实例时(如new User()); 2. 当调用类的静态方法时(如User.sayHello()); 3. 当访问类的静态变量(非final修饰)时(如int a = User.a); 4. 当通过反射调用类时(如Class.forName("com.example.User")); 5. 当初始化子类时,若父类未初始化,则先初始化父类; 6. 当 JVM 启动时,执行主类(含main方法的类)。 > 注意:以下场景不会触发初始化(称为 “被动引用”): 访问类的final静态变量(编译期已确定值,存储在常量池); 通过子类引用父类的静态变量(仅初始化父类); 创建类的数组(如User[] users = new User[10],仅创建数组对象,不初始化User类)。
毛林
2025年10月27日 21:01
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
Markdown文件
PDF文档(打印)
分享
链接
类型
密码
更新密码