【问题标题】:final variable initialized first最终变量首先初始化
【发布时间】:2019-07-23 16:57:15
【问题描述】:

我在这里阅读问题:Java : in what order are static final fields initialized?

根据答案

"除了最终类的变量和接口的字段 值是编译时常量首先初始化..."

我认为这是不正确的,因为以下将失败:

static {
    String y = x;
}

public static final String x = "test";

在静态块中,x 无法识别。如果这个答案是正确的,任何人都可以评论吗?

【问题讨论】:

    标签: java static initialization


    【解决方案1】:

    初始化的顺序 并没有改变 JLS 不允许您在各种情况下声明变量之前引用它们的事实。这在JLS§8.3.3 中有描述:

    在使用后以文本形式出现的类变量的使用有时会受到限制,即使这些类变量在范围内(第 6.3 节)。具体来说,如果以下所有条件都为真,则为编译时错误:

    • 类或接口 C 中的类变量声明在使用类变量后以文本形式出现;

    • 在 C 的类变量初始化器或 C 的静态初始化器中使用一个简单的名称;

    • 用法不在作业的左侧;

    • C 是包含使用的最内层类或接口。

    这就是您的代码出现此编译错误的原因:

    错误:非法前向引用

    JLS§12.4.2中确实定义了先初始化作为常量变量的静态字段的说法:

    1. 否则,记录当前线程正在为C初始化Class对象,释放LC。

      然后,初始化 C 的静态字段,它们是常量变量(第 4.12.4 节、第 8.3.2 节、第 9.3.1 节)。

    ...

    1. 接下来,按文本顺序执行类的类变量初始化程序和静态初始化程序,或者接口的字段初始化程序,就好像它们是单个块一样。

    如您所见,常量变量在第 6 步初始化,而其他变量在第 9 步初始化。

    这演示了行为:

    public class Example {
        static String y;
        static  {
             y = foo();
        }
    
        static String foo() {
            return x.toUpperCase();
        }
    
        public static final String x = "test";
    
        public static void main(String[] args) throws Exception {
            System.out.println(x);
            System.out.println(y);
        }
    }
    

    编译并输出:

    测试 测试

    相反,如果您更改 x 行使其不再恒定:

    public static final String x = Math.random() < 0.5 ? "test" : "ing";
    

    它编译,但随后失败,因为 xnull y = foo();


    为免生疑问:我建议使用方法来初始化这样的字段。 :-)

    【讨论】:

    • 非常好的例子,我很困惑如何在静态块中调用静态方法,而方法是在之后声明的,你能解释一下那部分吗?
    • @MohammadKarmi - 为了避免这个问题,我可能应该先定义它。 :-D JLS 中没有任何内容禁止这种形式的前向引用(前向引用方法而不是另一个字段),所以......它是允许的。我相信在初始化期间禁止前向引用是为了避免可能的循环情况(String x = y; String y = x;)。这对于方法来说不是问题,因为它们是类的一部分,并且在类加载后、静态初始化开始之前就存在。
    • @MohammadKarmi 在静态初始化器中,变量引用必须按拓扑排序,即每个初始化器只能引用它上面的变量,而不是它下面的变量。此规则可防止形成循环 (String a = b; String b = a;) 另一方面,可以按任何顺序声明和使用方法。不同之处在于方法的声明不会“做”任何事情。如果一个方法a() 引用了一个方法b(),而b() 引用了a(),那没关系,因为这两个方法都没有被调用,只是声明了。初始化器会立即被调用,所以循环是个问题。
    【解决方案2】:

    正如this answer 所说:

    ...它们按照它们在源中出现的顺序进行初始化。

    你是绝对正确的,你的例子失败了,因为你试图使用一个在使用后声明的变量。由于静态块是按源代码的顺序执行的,因此您是绝对正确的,应该建议并编辑该答案,因为此语句无效:

    除了最终类变量...首先被初始化。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-07-12
      • 2023-04-02
      • 1970-01-01
      • 1970-01-01
      • 2020-04-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多