学习类的加载机制
/**
类的加载机制(这里的类指泛类:普通类,接口,枚举,注解...等一切类)
一 JVM和类
1 当调用java命令运行java程序时,该命令会启动一个java虚拟机进程,
不管该java程序多么复杂,该程序启动了多少进程,它们都在该java虚拟机进程里.
2 JVM进程被终止(jvm进程结束,该进程在内存中的所有数据都丢失了)
-正常结束
-运行System.exit)或Runtime.getRuntime().exit()
-程序出现了错误或未捕捉的异常
-平台强制结束了jvm进程
(II) 类的加载机制,3个步骤
1 类的加载
2 类的额连接
3 类的初始化
二 类的加载
(I)概念: 类的加载是指将类的class文件读取到内存中,并为之创建一个java.lang.Class对象的过程.(class文件->Class对象的过程)
(当程序使用任何类都是如此,都会为之创建一个java.lang.Class对象,注意此时并没有执行类的初始化)
(II)类的加载由类的加载器完成(类加载器通常有JVM提供,当然也可以自定义类加载器,比如后面通过自定义类继承ClassLoader基类)
(III) 从不同来源加载类的二进制数据(通过不同的类加载器可以办到),类的二进制数据如下几种来源
1 从本地文件系统加载class文件(大多数类加载的方式)
2 从jar包中加载class文件(也很常见)
3 通过网络加载class文件
4 把一个java源文件动态编译,执行并加载
(VI) 注意: java虚拟机规范允许系统预先记载某些类,无需等到"首次使用"才加载该类
三 类的连接
(I)概念: 系统将类的加载生成的Class对象,在类的连接阶段将该类的二进制数据合并到JRE
分为三个阶段: 验证 -> 准备 -> 解析
1 验证: 验证被加载的类是否有正确的结构,并和其他类协调一致
2 准备: 为类的类变量分配内存,并设置默认的初始值.
3 解析:将类的二进制数据中的符号引用替换成直接引用.(虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程)
四 类的初始化(这里的类指一切类,泛类,一个泛类的初始化步骤前总是先初始化其直接父类)
概念: 类的初始化阶段,主要是执行类的初始化语句(静态代码块和声明类变量显式初始化的语句)
类的初始化语句(按定义顺序执行)
1 在声明类变量时指定初始值的语句
2 静态代码块
类变量显式初始化的2种方式
1 声明类变量时指定初始值
2 静态代码块中为类变量指定初始值
类的初始化步骤:其实就2步
1 加载和连接该类(假如这个类没有被加载和连接)
2 先初始化其直接父类(假如该类的直接父类还没有被初始化)只是一个判断的过程
3 执行该类初始化语句(假如该类中有初始化语句).
注意:
1 如上2只是一个判断的过程,其实还是1和3,真实的执行步骤: 简单13 复杂时111333
2 一个泛类的初始化步骤前总是先初始化其直接父类(假如该类的直接父类还没有被初始化)
五 类初始化的时机
如下6中情况将引发类或接口的初始化.
1 初始化某个类的子类
2 通过反射创建某个类或接口对应的Class对象
3 创建该类的实例
4 访问某个类的类变量
5 访问静态方法
6 直接使用java.exe命令运行某个主类(也是访问静态方法)
如下行为不会导致类的初始化(初始化该类的静态类)和执行"类.class;"语句
初始化某个类的静态内部类
初始化某个类的内部枚举
初始化某个类的内部接口
初始化某个类的内部注解
类.class
ClassLoader的loadClass(String)方法加载某个类仅仅是执行了类的加载阶段和类的连接阶段,并没有执行类的初始化阶段.
访问某个类的静态宏变量(小心,别忘了)
解释一下: 访问类的静态变量,如果它是final类型且在编译时就可以确定下来宏变量.(至于什么是宏变量需要复习一下)
已经成了宏变量,编译器在编译时直接会把这个类变量出现的地方替换成它的值
注意:
*/
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
class A {
static { System.out.println("A的静态代码块");}
static String name1 = "jack";
final static String name2 = "lucy";
static void test(){}
static void test2(BB bb){new BB();}
static class InnerA{
static String innerA = "mary";
static {System.out.println("A的静态内部类的静态代码块");}
}
enum InnerEnum{
SPIRNG;
static {System.out.println("A的内部枚举的静态代码块");}
}
interface InnerInterface{
String name = "tom";
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface InnerAnnotation {
String name() default "jerry";
}
}
@A.InnerAnnotation
public class 类的加载机制 {
static { System.out.println("================"); }
public static void main(String[] args) throws Exception{
// System.out.println(new A());//创建该类对象
// System.out.println(A.name1); //静态变量
// A.test();//静态方法
// System.out.println(Class.forName("com.china.school.reflect.A"));//反射
// System.out.println();//使用java.exe命令运行当前主类main方法,初始化这个当前主类
// System.out.println(new A(){});//初始化某个类的子类,本条语句初始化A的匿名内部类(也是A的子类)
/**
* 单独测试如上注释的打印语句都可以看到"A的静态代码块"字样在控制台输出.
* 全部打印如下语句,没有看到"A的静态代码块"字样在控制台输出,说明A类没有执行初始化步骤
* 总结: 如下行为将导致类的初始化
* 访问该类的静态变量,静态方法,创建该类的对象,初始化该类的子类,反射该类的Class对象,java.exe运行当前类的main方法.
* 如下行为不会: 初始化某类的静态类(静态内部类,内部枚举,内部接口,内部注解)和类.class
*
*/
System.out.println(A.InnerA.innerA);//初始化某个类的静态内部类
System.out.println(A.InnerEnum.SPIRNG);//初始化某个类的内部枚举
System.out.println(A.InnerInterface.name);//初始化某个类的内部接口
//初始化某个类的内部注解,获取某个类的内部注解实例
System.out.println(类的加载机制.class.getDeclaredAnnotation(A.InnerAnnotation.class).name());
System.out.println(A.class);
System.out.println(A.name2);
//访问类的静态变量,如果它是final类型且在编译时就可以确定下来宏变量.(至于什么是宏变量需要复习一下)
// 已经成了宏变量,编译器在编译时直接会把这个类变量出现的地方替换成它的值
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
Class<?> clazz = classLoader.loadClass("A");
System.out.println(clazz);
// ClassLoader的loadClass(String)方法加载某个类仅仅是执行了类的加载阶段和类的连接阶段,没有执行类的初始化阶段
}
}
输出结果如下:
================
A的静态内部类的静态代码块
mary
A的内部枚举的静态代码块
SPIRNG
tom
jerry
class com.china.school.reflect.A
lucy
class com.china.school.reflect.A
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报