【问题标题】:Static final variable along with static initializer block静态最终变量以及静态初始化块
【发布时间】:2013-09-30 14:08:33
【问题描述】:

我创建了一个类,其中包含:

  1. static final变量
  2. static 带有 System.out.println() 语句的初始化块

如果我从另一个类调用static final 变量,static 块不会执行。

据我所知,static 初始化程序块在类加载到内存时执行。

在这种情况下,内存级别发生了什么?

类没有加载到内存中吗?如果没有,其他类从哪里得到final static变量的地址?


案例 1: static 块不执行

class Test2 {
    static final int a = 20;

    static {
        System.out.println("one");
    }
}

案例 2: static执行

class Test2 {
    static final int a;

    static {
        a = 20;
        System.out.println("one");
    }
}

输出

class Test {
    public static void main(String[] args) {
        System.out.println(Test2.a);
    }
}
  • 案例一:

    20
    
  • 案例 2:

    one
    20
    

那么这两个层面发生了什么?

【问题讨论】:

  • 发布您的代码!
  • 不确定您的问题是什么。能否提供示例代码。
  • 描述代码几乎总是不如仅仅显示它 - 理想情况下是一个简短但完整的程序来演示问题。
  • 你也可以发布输出吗?

标签: java


【解决方案1】:

我的猜测是你的字段要么是原始类型要么是String,并且是用编译时常量表达式初始化的。

对于使用常量表达式初始化的静态最终字段(并且仅限此类字段) - 任何引用该字段的代码都会将常量值烘焙到其中,而不是通过会导致类初始化的静态字段。 “常量表达式”部分很重要。我们可以通过一个小型测试应用看到这一点:

class Fields {
    
    public static final String CONSTANT = "Constant";
    public static final String NON_CONSTANT = new String("Non-constant");
    
    static {
        System.out.println("Initializing");
    }
}

public class Test {
    public static void main(String arg[]) {
        System.out.println(Fields.CONSTANT);
        System.out.println(Fields.NON_CONSTANT);
    }
}

输出是:

Constant
Initializing
Non-constant

访问常量字段不需要需要初始化,但访问非常量字段需要。使用非 final 字段会产生相同的效果:基本上,它不再算作常数。

关于“这是一个常量”的信息被嵌入到声明字段的类中。例如,使用javap -c Fields 我们看到两个字段:

public static final java.lang.String CONSTANT;
  flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL
  ConstantValue: String Constant

public static final java.lang.String NON_CONSTANT;
  flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL

注意CONSTANT 字段元数据的ConstantValue 部分,NON_CONSTANT 字段元数据中缺少该部分。

请参阅section 15.28 of the JLS 了解更多关于常量表达式的构成。

Section 12.4.1 of the JLS 指定类的初始化时间:

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

  • T 是一个类,并创建了一个 T 的实例。

  • T是一个类,调用了T声明的静态方法。

  • 分配了一个由 T 声明的静态字段。

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

  • T 是一个顶级类(第 7.6 节),并且执行一个词法嵌套在 T(第 8.1.3 节)中的断言语句(第 14.10 节)。

(强调我的。)

【讨论】:

  • 实际上,当第一次类名出现在代码中时,我会阅读。该时间类加载到内存中。这是正确的吗?如果类加载时出错?
  • @deepak:这是错误的,正如你所看到的 - 有关何时初始化类的详细信息,请参阅我的编辑。
  • 第 12.4.1 节的第 3 条可能会让人相信 CONSTANT分配值“常量”并因此导致类初始化,但该字段改为 已初始化,因此子弹 3 不适用。 This wiki 可以帮助区分字段初始化和赋值。
  • 顺便说一句,同一部分中的第 5 条已经过时了。
  • @Ichiro:修复了子弹。我会留下你的评论来说明一切。
【解决方案2】:
  1. 静态最终字段是编译时常量,其值为 硬编码到目标类而不引用其 产地;

  2. 因此您的主类不会触发类的加载 包含该字段;

  3. 因此该类中的静态初始化程序不会被执行。

请参阅从定义中删除 final 的魔法。您将看到正在执行的静态初始化程序

【讨论】:

  • 这实际上并不是全部 - 正如您的第一点所暗示的那样,它不适用于 all 静态最终字段。有关详细信息,请参阅我的答案。
猜你喜欢
  • 1970-01-01
  • 2019-11-13
  • 1970-01-01
  • 1970-01-01
  • 2014-10-06
  • 2011-08-22
  • 2010-12-22
  • 2012-11-26
相关资源
最近更新 更多