繁体   English   中英

编译时常量和变量

[英]Compile-time constants and variables

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

我的理解正确吗?

编译时间常数必须是:

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

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

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

JLSfinal变量和常量进行了以下区分:

final变量

变量可以声明为final 一个final变量只能赋值一次。 如果final变量被赋值,则是编译时错误,除非它在赋值之前明确未赋值( 第 16 节(明确赋值) )。

一旦分配了final变量,它始终包含相同的值。 如果final变量持有对对象的引用,则对象的状态可能会因对对象的操作而改变,但变量将始终引用同一个对象。 这也适用于数组,因为数组是对象; 如果final变量持有对数组的引用,则数组的组成部分可能会因对数组的操作而改变,但该变量将始终引用同一个数组。

空白final是声明缺少初始化程序的final变量。

常数

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

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

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

编译时常量呢?

JLS不包含短语compile-time constant 然而,程序员经常交替使用术语编译时常量常量

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

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

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

但是 JLS 对变量施加了一些其他要求,使其成为“常量变量”(除了只是最终变量):

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

4.12.4. 最终变量 (JLS)

常量变量是使用常量表达式(§15.28)初始化的原始类型或字符串类型的最终变量。

15.28. 常量表达式

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

原始类型的文字和字符串类型的文字(§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 节),其包含的表达式是一个常量表达式。

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

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

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

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

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

至于第二个问题: y不是最终的,因此不是编译时常量,因为它可能在运行时发生变化。

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

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

私有整数 y = 10; // 这里 y 不是最终的

被编译器以与编译时常量相同的方式对待?

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

简单地说,编译器在编译时用指定的实际值替换引用,而不是使用引用参数。

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

IE。 编译时,编译器直接采用初始化值 5 进行编译,而不是使用引用变量“x”;

请检查这个解释

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM