1

我有以下场景包 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

我在想,只要我提到静态变量,静态块就会被执行。

有什么解释吗?

4

3 回答 3

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";
}

你会得到你期望的结果。

有一个很好的答案,解释了内联和 JLS 对编译时常量给出的保证。

于 2013-04-12T07:55:49.157 回答
3
String staticvar =StaticTest.STATIC_VAR; 

不加载类StaticTest。相反,编译器将常量的值内到. 所以在运行时,这段代码将被执行:MainClass

String staticvar = "Static Var"; 

JLS 将此称为“常量”:

原始类型或 String 类型的变量,它是 final 并使用编译时常量表达式(第 15.28 节)初始化,称为常量变量。

这意味着这StaticTest.init();是 VM 第一次必须实际加载类。这会导致执行静态块。

于 2013-04-12T07:54:20.730 回答
0

主要原因是您声明STATIC_VAR为常量值,它将被编译器内联而不是被引用。将代码更改为

public static /*final*/ String STATIC_VAR="Static Var";

你会得到你期望的行为。

请参阅第 12.4.1 节。当Java 语言规范初始化发生时:

类或接口类型 T 将在以下任何一项第一次出现之前立即初始化:

  • ...
  • 使用了由 T 声明的静态字段,并且该字段不是常量变量(第 4.12.4 节)。

inlining请参阅有关技术背景的常量值的其他答案。

于 2013-04-12T07:59:20.757 回答