编译时常量和变量

Java语言文档说:

如果将基本类型或字符串定义为常量并且在编译时已知该值,则编译器会将代码中的常量名称替换为其值。 这称为编译时常量。

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

private final int x = 10; 

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


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

 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. just final意味着它是一个常量但在运行时初始化
  3. 只是static意味着在运行时初始化
  4. 没有final是一个变量,不会被视为常量。

我的理解是否正确?

编译时间常数必须是:

  • 宣布决赛
  • 原语或字符串
  • 在声明中初始化
  • 用常量表达式初始化

所以private final int x = getX(); 不是恒定的。

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

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

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

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

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

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

某些机器上的private final int x = getX(); 可能会有很小的性能下降private final int x = getX(); 因为这将涉及至少一个方法调用(除了这不是编译时常量的事实),但正如你所说,它可以忽略不计 ,为什么还要麻烦?

至于第二个问题: y不是final,因此它不是编译时常量,因为它可能在运行时改变。

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

根据JLS,没有要求“常数变量”应该是静态的。

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

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

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

4.12.4。 最终变量(JLS)

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

15.28。 常数表达式

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

原始类型的文字和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)

括号表达式(第15.8.5节),其包含的表达式是常量表达式。

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

TypeNameforms的限定名称(第6.5.6.2节)。 引用常量变量的标识符(§4.12.4)。

JLS在final变量和常量之间做出以下区分:

final变量

变量可以声明为finalfinal变量只能分配一次。 如果指定了final变量,那么这是一个编译时错误,除非在赋值之前它是明确未分配的( §16(Definite Assignment) )。

一旦分配了final变量,它总是包含相同的值。 如果final变量包含对对象的引用,则可以通过对对象的操作来更改对象的状态,但该变量将始终引用同一对象。 这也适用于数组,因为数组是对象; 如果final变量包含对数组的引用,则数组的组件可能会被数组上的操作更改,但变量将始终引用相同的数组。

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

常量

常量变量是基本类型的final变量或用常量表达式初始化的String类型(第15.28节 )。

根据这个定义,我们可以看出常量必须是:

  • 宣布final
  • 原始类型或类型String
  • 在其声明中初始化(不是空白的final
  • 用常量表达式初始化

编译时常量怎么样?

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

如果final变量不符合上述标准被视为常数,则在技术上应将其称为final变量。

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

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