我有以下情况 package com.example.test;
public class StaticTest {
public static final String STATIC_VAR="Static Var";
static{
System.out.println("Static Block Called....");
}
public static void init(){}
}
package com.example.test;
public class MainClass {
public static void main(String[] args) {
System.out.println("Test static initialization");
String staticvar =StaticTest.STATIC_VAR;
System.out.println("Referred static variable--> "+ staticvar);
System.out.println("Calling static method");
StaticTest.init();
System.out.println("Static method invoked");
}
}
我得到的输出是
Test static initialization
Referred static variable--> Static Var
Calling static method
**Static Block Called....**
Static method invoked
我期待的输出是
Test static initialization
**Static Block Called....**
Referred static variable--> Static Var
Calling static method
Static method invoked
我在想,只要我引用静态变量,静态块就会被执行。
任何解释?
答案 0 :(得分:3)
String staticvar =StaticTest.STATIC_VAR;
不加载课程StaticTest
。相反,编译器将常量的值内联到MainClass
。所以在运行时,这段代码将被执行:
String staticvar = "Static Var";
JLS称之为“constant”:
基本类型或类型String的变量,即final和 用编译时常量表达式(第15.28节)初始化,是 称为常数变量。
这意味着StaticTest.init();
是VM第一次必须实际加载类。这导致执行静态块。
答案 1 :(得分:3)
因为变量是public static final
,所以它由编译器内联。
对它的所有引用都被实际值替换,因为它不能改变,这称为编译时间常量。
您的代码基本上被编译为:
System.out.println("Test static initialization");
String staticvar = "Static Var";
如果将值赋给方法的返回值 -
public static final String STATIC_VAR=getStaticVar();
private static String getStaticVar() {
return "Static Var";
}
您将获得您期望的结果。
有一个good SO answer解释内联和JLS对编译时常量给出的保证。
答案 2 :(得分:0)
主要原因是您将STATIC_VAR
声明为常量值,它将由编译器内联而不是被引用。将代码更改为
public static /*final*/ String STATIC_VAR="Static Var";
你会得到你期望的行为。
请参阅Java语言规范的§12.4.1. When Initialization Occurs:
类或接口类型T将在第一次出现以下任何一个之前立即初始化:
- ...
- 使用T声明的静态字段,字段不是常量变量(§4.12.4)。
请参阅inlining
关于技术背景的常量值的其他答案。