类加载
类加载包括三个阶段,加载、连接、初始化,
加载:查找并加载字节码文件。
链接:分为三个阶段:①验证:文件格式验证,元数据验证,②准备:为静态变量分配内存,并设定默认值;③解析:把类的符号引用转为直接引用。
初始化:通过<clinit>方法为类的静态变量赋予正确的初始值或执行静态代码块逻辑,如果只有静态成员,但没有赋值操作,不会有<clinit>方法。
其中当final static 修饰的是基础数据类型和String数据类型时,在编译时Javac会为该常量生成ConstantValue属性,在准备阶段虚拟机便会根据ConstantValue为常量设置相应的值,如果该变量没有被final修饰,或者并非基本类型及字符串,则选择在类构造器中进行初始化。
final static 修饰基本数据类型和String
public class Test {
public static void main(String[] args) {
System.out.println(A.a);
System.out.println(B.b);
}
}
class A{
public final static int a = 10;
static {
System.out.println("A静态代码块执行");
}
}
class B{
public final static String b = "xxx";
static {
System.out.println("B静态代码块执行");
}
}
输出
10
xxx
结果不会触发类的初始化和静态代码块的执行。
final static 修饰非基本类型及String
public class Test {
public static void main(String[] args) {
System.out.println(C.obj);
}
}
class C{
public final static Object obj = new Object();
static{
System.out.println("C代码块被执行");
}
}
输出
C代码块被执行
java.lang.Object@4554617c
没有final,只有static修饰
public class Test {
public static void main(String[] args) {
System.out.println(D.obj);
System.out.println(E.e);
System.out.println(F.f);
}
}
class D{
public static Object obj = new Object();
static{
System.out.println("D代码块被执行");
}
}
class E{
public static int e = 10;
static {
System.out.println("E静态代码块执行");
}
}
class F{
public static String f = "xxx";
static {
System.out.println("F静态代码块执行");
}
}
输出
D代码块被执行
java.lang.Object@4554617c
E静态代码块执行
10
F静态代码块执行
xxx
以上除了final static修饰,其余的在准备阶段只给定了默认值,在初始化阶段才赋予了相对应的值,并且执行了静态代码块。
结论:
当final static 修饰的是基础数据类型和String数据类型时,不会触发类的初始化和static代码块的执行
final static 修饰非基本类型及String和只有static修饰会触发类的初始化。