为什么Java常量被声明为静态的?

40

为什么 Java 中的常量要声明为静态变量?

class Foo{
    static final int FII = 2 ;
}

我理解final的用法,但为什么它必须是静态的呢?为什么它应该是一个类变量而不是实例变量?


10
为什么你想让多个实例拥有各自的 FII 副本? - Bart Kiers
1
我可以很好地声明 final int FII = 2; - Jon Lin
1
@JonLin:你可以这样做,但不应该这样做,因为它不会增加价值,只会增加内存使用量。唯一的例外是在构造函数调用中初始化常量 - 然后每个实例都可以有自己的常量值(在构造函数运行后变成常量),并且可以具有类似'constructionTime'的值。 - Mathias Bader
4个回答

115
如果一个常量不是静态的, Java会在类的每个对象中为该常量分配内存(即每个对象都有一份该常量的副本)。
如果一个常量是静态的,那么该类只有一个该常量的副本 (即每个类只有一份该常量的副本)。
因此,如果常量只有一个值,它应该被声明为静态。
如果该常量可能对于每个对象具有不同的值,例如对象创建时间,则不应将其声明为静态。

1
如果常量在每个对象中可能具有不同的值,那么根据定义它就不是一个常量。 - Jezor
2
@Jezor 不,它是常量。同一类对象拥有相同常量但不同值是可以的。例如对象的创建时间。 - Rain
我了解。称它为“常量”可能在技术上是正确的,但听起来相当奇怪。因此,在Java中,每个通过关键字 final 声明的不可变对象都是一个常量,无论其作用域如何。 - Jezor
@SameerSinha 不管常量值是在编译时还是运行时定义的,这都不会改变它是常量(一旦初始化后无法更改的值)的事实。例如,在 PHP 语言中,我们有 constdefine,一个将在编译时评估,另一个将在运行时评估,但最重要的是它们都创建了一个常量。 - Rain
如果是这样,那么当对象被释放时,常量属性值仍然在内存中,对吗? - William Hu
显示剩余2条评论

45
如果它可以因为某个类的实例而变化,那么它显然不是一个“常量”。如果每个Math实例都有不同的pi值(尽管Math甚至不允许构造实例),或者每个String实例都有不同的大小写不敏感排序,那将意味着什么?

14

这样做是为了让你能够在没有该类实例的情况下访问它们。

要求创建一个实例才能访问常量字段有些浪费资源。


3
为什么Java常量要声明为静态的?
从技术上讲,它们并不是。JLS将常量定义为最终的常量表达式(在Java中非正式地称为编译时常量表达式)。这意味着一个被声明为最终变量的变量被初始化为一个常量表达式,即没有静态-https://docs.oracle.com/javase/specs/jls/se8/html/jls-4.html#jls-4.12.4 常量变量是指用常量表达式初始化的原始类型或字符串类型的最终变量。
但规范没有涉及Java中所有内容的“常量类型”,包括类和对象——因此Jon Skeet关于Math示例的回答缺少了一部分,Math类被认为是常量,因为您不能通过将其构造函数设置为私有来实例化它。

http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/share/classes/java/lang/Math.java -

这是一个指向Java Math类源代码的链接。
public final class Math {


    /**

     * Don't let anyone instantiate this class.

     */

    private Math() {}

    public static final double PI = 3.14159265358979323846;

}

为了支持类型本身的常量性,您需要确保其状态也是常量的,但仅适用于可变的成员并且暴露给类型外部的成员。
例如,字面值PI是公开的,因此可以从类型外部访问。因此,为了确保它不会从外部更改,它被设置为final,并且还是静态的,因此它将成为Class Class<Math>实例的一部分,并可以在Math之外使用而无需(显式)实例化类。

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