classloader
# 类加载过程
「加载」
将类的.class 文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在内存上创建一个 java.lang.Class 对象用来封装类在方法区内的数据结构作为这个类的各种数据的访问入口
「验证」
主要是为了确保 class 文件中的字节流包含的信息是否符合当前 JVM 的要求,且不会危害 JVM 自身安全,比如校验文件格式、是否是 cafe baby 魔术、字节码验证等等
「准备」
为类变量分配内存并设置类变量(是被 static 修饰的变量,变量不是常量,所以不是 final 的,就是 static 的)初始值的阶段。这些变量所使用的内存在方法区中进行分配。比如
private static int age = 26; 类变量 age 会在准备阶段过后为 其分配四个(int 四个字节)字节的空间,并且设置初始值为 0,而不是 26
若是 final 的,则在编译期就会设置上最终值
「解析」
JVM 会在此阶段把类的二进制数据中的符号引用替换为直接引用
「初始化」
初始化阶段是执行类构造器 <clinit>() 方法的过程,到了初始化阶段,才真正开始执行类定义的 Java 程序代码(或者说字节码 )。比如准备阶段的那个 age 初始值是 0,到这一步就设置为 26
「使用」
对象都出来了,业务系统直接调用阶段
「卸载」
用完了,可以被 GC 回收了
# 类加载器种类以及加载范围
# 双亲委派
# 常见笔试题
# Illegal forward reference
public class Test1 {
static {
// 编译没报错
i = 2;
// 编译报错Illegal forward reference
System.out.println(i);
}
private static int i =1;
}
2
3
4
5
6
7
8
9
错误的原因:
静态变量的初始化顺序: 在 Java 中,静态变量的初始化顺序是从上到下依次进行的。静态块中的代码会在静态变量初始化时执行。然而,在静态变量 i 完全初始化之前,你在静态块中使用了它,这就导致了非法的前向引用(Illegal forward reference)
为什么
i = 2没报错?: 静态变量在声明之前的赋值操作是允许的,但在其声明之前读取或使用它的值(如通过System.out.println(i))是不允许的。也就是说,赋值可以,但访问或读取是非法的
将 i 的声明提前到静态块之前即可避免该问题
public class Test1 {
private static int i = 1; // 将静态变量声明放到前面
static {
// 编译不会报错
i = 2;
// 编译不会再报错
System.out.println(i);
}
}
2
3
4
5
6
7
8
9
在这个修复后的代码中,静态变量 i 已经被定义并初始化为 1,然后在静态块中修改为 2,并成功打印出结果