类初始化规则
虚拟机规范则严格规定了有且只有5种情况必须立即对类进行“初始化”(而加载、验证、准备自然需要
在此之前开始):
- 遇到new、getstatic、putstatic或invokestatic这4条字节码指令时,如果类没有进行过初始化,则需要先触发其初始化。生成这4条指令的最常见的Java代码场景是:使用new关键字实例化对象的时候、读取或设置一个类的静态字段(被final修饰、已在编译期把结果放入常量池的静态字段除外)的时候,以及调用一个类的静态方法的时候。
- 使用java.lang.reflect包的方法对类进行反射调用的时候,如果类没有进行过初始化,则需要先触发其初始化。
- 当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化。
- 当虚拟机启动时,用户需要指定一个要执行的主类(包含main()方法的那个类),虚拟机会先初始化这个主类。
- 当使用JDK
1.7的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,则需要先触发其初始化。
类初始化需要做的就是执行静态代码块,完成静态变量的赋值
静态字段、静态代码段,字节码层面会生成clinit方法
读取静态字段
test1
public class Test_1 {
public static void main(String[] args) {
Test_1_Enum v = Test_1_Enum.V;
System.out.printf(Test_1_B.str);
}
//输出
// Enum Static Block
// A Static Block
// A str
//jvm中类的加载是懒加载的,并没有创建Test_1_B的对象,但是经过了父类的对象,A里面已经有str没必要加载B
}
class Test_1_A {
public static String str = "A str";
static {
System.out.println("A Static Block");
}
}
class Test_1_B extends Test_1_A {
static {
System.out.println("B Static Block");
}
}
enum Test_1_Enum {
V;
static {
System.out.println("Enum Static Block");
}
}
test2
public class Test_2 {
public static void main(String[] args) {
System.out.printf(Test_2_B.str);
//输出
// A Static Block
//B Static Block
//B str
//B里面有str需要加载B类,A是B的父类也需要加载
}
}
class Test_2_A {
static {
System.out.println("A Static Block");
}
}
class Test_2_B extends Test_2_A {
public static String str = "B str";
static {
System.out.println("B Static Block");
}
}
test3
public class Test_3 {
public static void main(String[] args) {
System.out.printf(Test_3_B.str);
//输出
// A Static Block
// B Static Block
// B str
//B里面有str需要加载B类,A是B的父类也需要加载
}
}
class Test_3_A {
public static String str = "A str";
static {
System.out.println("A Static Block");
}
}
class Test_3_B extends Test_3_A {
public static String str = "B str";
static {
System.out.println("B Static Block");
}
}
test4
public class Test_4 {
public static void main(String[] args) {
Test_4_A arrs[] = new Test_4_A[1];
System.out.println("end");
//输出end 只是定义个一个数组类型,数组类是jvm运行时创建的,A类不会被加载
}
}
class Test_4_A {
static {
System.out.println("Test_4_A Static Block");
}
}
test5
public class Test_5 {
public static void main(String[] args) {
Test_5_A obj = new Test_5_A();
System.out.println("end");
//输出
// Test_5_A Static Block
// end
//new 了一个新的对象需要加载A类,执行静态代码块
}
}
class Test_5_A {
static {
System.out.println("Test_5_A Static Block");
}
}
test6
public class Test_6 {
public static void main(String[] args) {
System.out.println(Test_6_A.str);
//输出 A str
//final是常量,将常量写入了stringTable常量池。没有加载A类
}
}
class Test_6_A {
public static final String str = "A Str";
static {
System.out.println("Test_6_A Static Block");
}
// public static final String str = "A Str";
}
test7
public class Test_7 {
public static void main(String[] args) {
System.out.println(Test_7_A.uuid);
//输出
//Test_7_A Static Block
//3afa6977-6ca5-4267-90aa-022ee34ee740
//uuid是动态生成,无法写入常量池,所以需要加载,jvm底层应该是做了特殊处理
}
}
class Test_7_A {
public static final String uuid = UUID.randomUUID().toString();
static {
System.out.println("Test_7_A Static Block");
}
}
test8
public class Test_8 {
static {
System.out.println("Test_8 Static Block");
}
public static void main(String[] args) throws ClassNotFoundException {
Class<?> clazz = Class.forName("com.demo.hxl.classload.Test_8_A");
System.out.println("end");
}
}
class Test_8_A {
static {
System.out.println("Test_8_A Static Block");
}
}
//输出
//Test_8 Static Block
//Test_8_A Static Block
//end
//反射也会加载静态代码块
test9
扫描二维码关注公众号,回复:
13132852 查看本文章
public class Test_21 {
public static void main(String[] args) {
Test_21_A obj = Test_21_A.getInstance();
System.out.println(Test_21_A.val1);
System.out.println(Test_21_A.val2);
}
}
class Test_21_A {
public static int val1 = 0;
public static int val2 = 1;
public static Test_21_A instance = new Test_21_A();
Test_21_A() {
val1++;
val2++;
}
public static Test_21_A getInstance() {
return instance;
}
}
//输出
//1
//2
//代码执行的顺序和定义的顺序是保持一致的
test10
public class Test_22 {
public static void main(String[] args) {
Test_22_A obj = Test_22_A.getInstance();
System.out.println(Test_22_A.val1);
System.out.println(Test_22_A.val2);
}
}
class Test_22_A {
// public static int val1;
public static Test_22_A instance = new Test_22_A();
Test_22_A() {
val1++;
val2++;
System.out.println("val1 = " + val1 + "\t val2 = " + val2);
}
public static int val1 = 2;
public static int val2 = 0;
public static Test_22_A getInstance() {
return instance;
}
}
//输出
//val1 = 1 val2 = 1
// 2
// 0
//
//val1和val2初始化是都为0 ,构造方法时++,变成都为1,然后被覆盖```