如何用Java处理静态final和static成员?

时间:2019-02-18 19:51:10

标签: java static final

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

例如:m0bius在How does the static keyword work in Java?中告诉我们(在“何时创建此副本[ed。静态变量]?”部分中),静态变量是在运行时创建的。但是,如果我检查https://en.wikipedia.org/wiki/Class_variable(“静态成员变量和静态成员函数”一节),则可以告诉我在某些语言中情况相反,而在其他语言中情况相同。

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

  • 在Java中,类的静态是在运行时还是在编译时创建的?
  • 在Java中,类的最终静态变量是在运行时还是在编译时创建的?

我所说的编译时间与运行时间:

  • 编译时间:编译源代码的时间(因此创建.class文件的时间)
  • 运行时间:程序实际运行的时间

一些仅用于此问题的代码:

// 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

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

1 个答案:

答案 0 :(得分:4)

  

在Java中,是在运行时还是在编译时创建的类的静态变量?

在Java中,编译时不会“创建”变量,如果创建是指分配和初始化。它们都是在运行时创建的。它们是static还是static final与分配它们的 无关。

  

但是我会期望最终的静态变量PI将在编译时创建(因此它将被“嵌入”在.class文件中),因为我认为这样做会有更好的性能...

这不是Java中的工作方式。当类文件被“编译”时,它们实际上基本上已经被编码了。当然,已经完成了一些工作,但是我们认为编译的很大一部分(就C语言而言)是在运行时发生的。当我们查看优化和内联时,尤其如此。

编译器会做一些准备工作,例如能够在可能的情况下提前计算字段(静态或实例)的值。例如,如果您按如下方式定义字段,则乘法将在编译时完成:

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

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

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

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

  

如果将在运行时同时创建静态变量和最终静态变量,则在Main的第一个版本中,不会创建类MyClass的两个静态变量(实例和PI)。

在您的示例中,在第一次加载时分配并初始化了static字段(是否为final)。因此,在您的第一个Main中,instancesPI static字段将不会像您提到的那样创建。在您的第二个示例中。一旦引用MyClass,将加载类文件并创建static字段。