【问题标题】:How are static final and static members handled in Java?Java 中如何处理静态最终成员和静态成员?
【发布时间】:2019-02-18 19:51:09
【问题描述】:

我已经在 Stack Overflow 和网络上的其他地方看到了一些关于静态变量的内容。但是,我并不清楚答案。当我认为我找到了答案时,其他一些来源与该陈述相矛盾(或者至少,我认为确实如此)。

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

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

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

我的意思是编译时间与运行时间:

  • 编译时间:编译源代码时(即创建.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

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

【问题讨论】:

  • Java 中的一切都是在运行时创建的,因为 Java 是一种解释型语言。您真正要问的是如何在堆栈和堆上分配内存。
  • 这些都不能编译,因为 PI 没有类型。我想你的意思是双重的。静态变量与类相关联,而不是与实例相关联。最终引用不能更改,但如果它是可变的,则可以更改它们的状态。您对性能的担忧无关紧要。
  • @Dylan,谢谢。
  • @duffymo,问题解决了:我确实忘记写双精度了!

标签: java static final


【解决方案1】:

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

在 Java 的编译时没有“创建”变量,如果创建意味着分配和初始化。它们都是在运行时创建的。它们是static 还是static final何时它们被分配无关。

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

这不是它在 Java 中的工作方式。当类文件被“编译”时,它们实际上主要是被编码的。肯定有一些工作已经完成,但我们认为编译的超大比例(就 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)的分配和初始化都是在运行时完成的。

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-20
    • 1970-01-01
    • 1970-01-01
    • 2011-05-08
    • 2016-12-21
    • 2016-01-13
    相关资源
    最近更新 更多