静态常量变量和静态初始化块

6
我创建了一个类,其中包含:
  1. static final变量
  2. 带有System.out.println()语句的static初始化块
如果我从另一个类调用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);
    }
}
  • Case 1:

    20
    
  • Case 2:

    one
    20
    

那么在这两个级别上会发生什么?


6
发表你的代码! - RandomQuestion
1
不确定您的问题是什么。您能否提供一段示例代码? - Aniket Thakur
描述代码几乎总是不如展示代码好 - 理想情况下,展示一个短而完整的程序以演示问题。 - Jon Skeet
你能否也发布输出结果? - David says Reinstate Monica
2个回答

12
我猜测你的字段要么是原始类型,要么是String类型,并且使用编译时常量表达式进行初始化。
对于使用常量表达式初始化的静态final字段(仅限这种字段) - 任何引用该字段的代码都将在其中嵌入常量值,而不是通过静态字段进行类初始化。但需要注意“常量表达式”这部分。我们可以通过一个小测试应用程序来看到这一点:
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字段元数据中缺失。
有关什么构成常量表达式的更多信息,请参见JLS第15.28节JLS第12.4.1节指定何时初始化类:
一个类或接口类型T将在以下任一事件的第一次出现之前立即初始化: - T是一个类并且创建了T的实例。 - T是一个类并且调用了T声明的静态方法。 - 由T声明的静态字段被赋值。 - 使用了由T声明的静态字段且该字段不是常量变量(§4.12.4)。 - T是一个顶层类(§7.6),并且在T内(§8.1.3)词法嵌套的断言语句(§14.10)被执行。
(强调是我的。)

实际上,当类名第一次出现在代码中时,我读取它。那时候,类会加载到内存中。这是正确的吗?如果不正确,那么类是什么时候加载的? - Deepak
1
@deepak:那是错误的,正如你所看到的-请查看我的编辑以了解类何时被初始化的详细信息。 - Jon Skeet
12.4.1节的第3个要点可能会让人们相信CONSTANT被赋予了值“Constant”,从而导致类初始化,但实际上该字段是被初始化的,因此第3个要点不适用。 可以参考此wiki来区分字段初始化和赋值。 - user9514304
顺便提一下,同一部分的第5个项目已经失控了。 - user9514304
@Ichiro:修复了这个问题。我会让你的评论自己说明。 - Jon Skeet

11
  1. 静态final字段是一个编译时常量,其值已经硬编码到目标类中而不需要对其来源的引用;

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

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

看看神奇的事情,从定义中去掉 final。你将看到静态初始化程序被执行。


2
这并不是整个故事 - 它并不适用于所有静态final字段,正如你的第一个观点所暗示的那样。请参阅我的答案以获取更多详细信息。 - Jon Skeet

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接