Java 中如何处理 static final 和 static 成员?

How are static final and static members handled in Java?

我已经在 Stack Overflow 和网络上的其他地方看到了一些关于静态变量的信息。但是我不清楚答案。当我认为我找到了答案时,其他一些来源与该声明相矛盾(或者至少,我认为是这样)。

例如:m0bius 在 (in the section "When is this copy [ed. the static variable] created?") that static variables are created at run time. But if I check https://en.wikipedia.org/wiki/Class_variable 中告诉我们(第 "Static member variables and static member functions" 节)它告诉我在某些语言中情况相反,而在其他语言中情况相同。

我这里的问题可以分为两个不同的问题:

编译时间与 运行 时间的对比:

部分代码仅用于本题:

// MyClass.java
public class MyClass {
    public static int instances;
    public final static double PI = 3.14159265359

    public MyClass() {
        instances++;
    }
    // ...
}

// Main.java ; version 1
public class Main {
    public static void main(String args[]) {
        System.out.println("I am doing nothing with MyClass");
    }
}
// OUTPUT: I am doing nothing with MyClass

// Main.java ; version 2
public class Main {
    public static void main(String args[]) {
        System.out.println("PI = " + MyClass.PI);
        MyClass obj1 = new MyClass();
        MyClass obj2 = new MyClass();
        System.out.println("instances = " + MyClass.instances);
    }
}
OUTPUT:
3.14159265359
2

如果 staticfinal static 变量都将在 运行 时创建,则在 Main 的第一个版本中两者都不会class MyClass 的两个静态变量(实例和 PI)都将被创建。但我希望 final static 变量 PI 会在编译时创建(所以它在 .class 文件中是 "embedded")因为我认为这会有更好的性能(无论 class MyClass 发生什么,PI 将始终为 3.14159265359,因此最好将其放入二进制文件中)。
这可能与静态变量相同,但它可能会在整个程序中发生变化。

In Java, are static variables of a class created at run time or at compile time?

编译时 Java 中没有变量 "created",如果创建是指分配和初始化。它们都是在运行时创建的。它们是 static 还是 static final 何时 分配它们无关。

But I would kind of expect that the final static variable PI would be created at compile time (so it would be "embedded" in the .class-file) because I think this would have a better performance...

Java 不是这样的。当 class 文件是 "compiled" 时,它们实际上大部分都在被编码。当然有一些工作已经完成,但我们所认为的编译(就 C 等语言而言)的很大一部分是在运行时发生的。当我们查看优化和内联时尤其如此。

编译器确实做了一些准备工作,例如如果可能的话能够提前预先计算字段(静态或实例)的值。例如,如果你像下面这样定义你的字段,那么乘法将在编译时完成:

private long timeoutMillis = 10 * 24 * 3600 * 1000;

对于 Strings 也是如此,编译器会尽可能将常量字符串附加在一起。以下代码在运行时不使用 StringBuilder,而是在编译时创建一个 String

private static final String DEFAULT_HEADER = "A long headers that is " +
   "broker into two lines\n";

但是,在这两种情况下,字段(long timeoutMillisString DEFAULT_HEADER)的分配和初始化都是在运行时完成的。

If both static and final static variables would be created at run time, in the Main's first version neither of both static variables (instances and PI) of class MyClass would be created.

在您的示例中,static 字段(final 或不)在 class 被 加载 时分配和初始化第一次。因此,在您的第一个 Main 中, instancesPI static 字段将永远不会像您提到的那样创建。在你的第二个例子中。一旦引用 MyClass,就会加载 class 文件并创建 static 字段。