编译时常量和变量

时间:2012-01-31 16:18:27

标签: java performance constants final compile-time-constant

Java语言文档说:

  

如果将基本类型或字符串定义为常量和值   在编译时已知,编译器替换常量名称   代码中的任何地方都有它的价值。这称为编译时   恒定。

我的理解是,如果我们有一段代码:

private final int x = 10;

然后,编译器将用文字x替换代码中每次出现的10


但假设常量在运行时初始化:

private final int x = getX(); // here getX() returns an integer value at run-time.

与编译时常量相比,是否会出现性能下降(无论可能忽略不计)?


另一个问题是下面的代码行是否是

private int y = 10; // here y is not final

被编译器以与编译时常量相同的方式处理?


最后,我从答案中理解的是:

  1. final static表示编译时常量
  2. 只是final表示它是常量但在运行时初始化
  3. 只是static表示在运行时初始化
  4. 没有final
  5. 是一个变量,不会被视为常量。
  6. 我的理解是否正确?

8 个答案:

答案 0 :(得分:50)

编译时间常数必须为:

  • 宣布最终
  • primitive或String
  • 在声明中初始化
  • 使用常量表达式初始化

所以private final int x = getX();不是常数。

对于第二个问题private int y = 10;不是常数(在这种情况下是非最终的),因此优化器无法确定该值将来不会改变。所以它不能像恒定值那样优化它。答案是:不,它的处理方式与编译时间常数不同。

答案 1 :(得分:2)

JLSfinal变量和常量之间做出以下区分:

final变量

  

变量可以声明为finalfinal变量可能只是。{   分配给一次。如果final变量是,则编译时错误   分配到,除非它在...之前完全未分配   作业(§16 (Definite Assignment))。

     

一旦分配了final变量,它总是包含相同的变量   值。如果final变量包含对象的引用,那么   可以通过对对象的操作来改变对象的状态,但是   变量将始终引用同一个对象。这也适用   到数组,因为数组是对象;如果final变量持有a   引用数组,然后可以更改数组的组件   通过对数组的操作,但变量将始终引用   相同的数组。

     

空白final 是一个final变量,其声明缺少   初始化程序。

常数

  

常量变量是原始类型或类型的final变量   使用常量表达式(§15.28)初始化的String

从这个定义中,我们可以看出常数必须是:

  • 声明final
  • 的原始类型或类型String
  • 在其声明中初始化(不是空白final
  • 使用constant expression
  • 初始化

编译时常量怎么样?

JLS不包含短语编译时常量。但是,程序员经常使用术语编译时常量常量

如果final变量不符合上述标准,则视为常量,从技术上讲,它应称为final变量。

答案 2 :(得分:1)

可能 (除了这不是编译时常量这一事实)但正如你所说,它会可以忽略不计所以为什么要这么麻烦?

至于第二个问题:private final int x = getX();不是最终的,因此它不是编译时常量,因为它可能在运行时发生变化。

答案 3 :(得分:1)

final关键字表示变量将初始化一次且仅一次。一个真正的常量也需要声明static。 因此,编译器都不会将您的示例视为常量。尽管如此,final关键字告诉您(以及编译器)您的变量将仅被初始化一次(在构造函数中或字面上)。 如果需要在编译时分配它们的值,则字段必须是静态的。

性能并没有真正受到影响,但请记住,原始类型是不可变的,一旦你创建了它,它将在内存中保存该值,直到垃圾收集器将其删除。 因此,如果你有一个变量y = 1;然后你在内存中将其更改为y = 2;,那么JVM将同时拥有这两个值,但你的变量将“指向”后者。

  

private int y = 10; //这里y不是最终的

     

被编译器以与编译时间常量相同的方式处理?

没有。这是一个实例变量,在运行时创建,初始化使用。

答案 4 :(得分:1)

根据JLS,并不要求“常量变量”应该是静态的。

所以“常量变量”可能是静态的或非静态的(实例变量)。

但是JLS将变量的其他一些要求强加为“常量变量”(除了最终):

  • 只是字符串或原始
  • 仅初始化内联,因为它是最终的,并且不允许空白最终
  • 使用“常量表达式”=“编译时常量表达式”初始化(参见下面的JLS引用)

4.12.4. final Variables (JLS)

  

常量变量是基本类型或类型String的最终变量,使用常量表达式(第15.28节)进行初始化。

15.28. Constant Expressions

  

编译时常量表达式是表示值的表达式   原始类型或不突然完成的字符串,是   仅使用以下内容撰写:

     

原始类型的文字和String类型的文字(§3.10.1,   §3.10.2,§3.10.3,§3.10.4,§3.10.5)

     

转换为基本类型并转换为String类型(第15.16节)

     

一元运算符+, - ,〜和! (但不是++或 - )(§15.15.3,   §15.15.4,§15.15.5,§15.15.6)

     

乘法运算符*,/和%(§15.17)

     

加法运算符+和 - (§15.18)

     

移位运算符<<,>>和>>> (§15.19)

     

关系运算符<,< =,>和> =(但不是instanceof)   (§15.20)

     

等于运算符==和!=(§15.21)

     

按位和逻辑运算符&,^和| (§15.22)

     

条件和操作员&&和条件或运算符||   (§15.23,§15.24)

     

三元条件运算符? :(§15.25)

     

包含表达式为a的括号表达式(第15.8.5节)   不断表达。

     

引用常量变量的简单名称(第6.5.6.1节)(§4.12.4)。

     

TypeName表单的限定名称(第6.5.6.2节)。标识符   参考常数变量(§4.12.4)。

答案 5 :(得分:1)

请记住,在以下代码中,x不是编译时间常数:

public static void main(String[] args) {
     final int x;
     x= 5;
}

答案 6 :(得分:0)

private final int x = getX(); 将在您的对象第一次被声明时调用。性能“下降”将取决于getX(),但这不是造成某些瓶颈的事情。

答案 7 :(得分:-3)

每个原始Literal和String Literal都是一个编译时常量。

请参阅:https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.28