java调用类中的静态变量时类中静态代码块什么情况会执行以及类的初始化问题?

类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括:加载、验证、准备、解析、初始化、使用和卸载七个阶段。
类初始化是类加载过程的最后一个阶段,到初始化阶段,才真正开始执行类中的Java程序代码。虚拟机规范严格规定了有且只有5种情况必须立即对类进行初始化:

第一种:遇到new、getstatic、putstatic、invokestatic这四条字节码指令时,如果类还没有进行过初始化,则需要先触发其初始化。生成这四条指令最常见的Java代码场景是:使用new关键字实例化对象时、读取或设置一个类的静态字段(static)时(被static修饰又被final修饰的,已在编译期把结果放入常量池的静态字段除外)、以及调用一个类的静态方法时。
第二种:使用Java.lang.refect包的方法对类进行反射调用时,如果类还没有进行过初始化,则需要先触发其初始化。
第三种:当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化。
第四种:当虚拟机启动时,用户需要指定一个要执行的主类,虚拟机会先执行该主类。
第五种:当使用JDK1.5支持时,如果一个java.langl.incoke.MethodHandle实例最后的解析结果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,则需要先触发其初始

执行代码:

package com.spring.partise;

import java.util.Random;

class A{
    static final int numA = Main.rand.nextInt(100);
    static{
        System.out.println("I am A");
    }
}
class B{
    static final int numB = 2;
    static{
        System.out.println("I am B");
    }
}
class C{
    static int numC = 3;
    static{
        System.out.println("I am C");
    }
}
public class Main {
    static Random rand = new Random();
    public static void main(String[] args) throws Exception {
        System.out.println("---------------------------");
        System.out.println(A.numA);//会执行static中的方法
        System.out.println("---------------------------");
        System.out.println(B.numB);//不会执行static中的方法
        System.out.println("---------------------------");
        System.out.println(C.numC);
    }
}

执行结果:

---------------------------
I am A
22
---------------------------
2
---------------------------
I am C
3

总结:如果一个static final值是“编译期常量”,就像static final int numB = 2;那样,那么这个值不需要对B类进行初始化就可以读取。但是,如果只是将一个域设置为static和final的,那不一足以确保这种行为,例如,对static final int numA = Main.rand.nextInt(100);的访问将强制进行类的初始化,因为它不是一个编译期常量。
如果一个static 域不是final,那么在对它访问时,总是要求在它被读取之前,要先进行链接(为这个域分配存储空间)和初始化(初始化该存储空间)就像static int numC = 3;那样!!!

猜你喜欢

转载自blog.csdn.net/dreamzuora/article/details/80188708