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
被编译器以与编译时常量相同的方式处理?
最后,我从答案中理解的是:
final static
表示编译时常量final
表示它是常量但在运行时初始化static
表示在运行时初始化final
的我的理解是否正确?
答案 0 :(得分:50)
编译时间常数必须为:
所以private final int x = getX();
不是常数。
对于第二个问题private int y = 10;
不是常数(在这种情况下是非最终的),因此优化器无法确定该值将来不会改变。所以它不能像恒定值那样优化它。答案是:不,它的处理方式与编译时间常数不同。
答案 1 :(得分:2)
JLS在final
变量和常量之间做出以下区分:
final
变量变量可以声明为
final
。final
变量可能只是。{ 分配给一次。如果final
变量是,则编译时错误 分配到,除非它在...之前完全未分配 作业(§16 (Definite Assignment))。一旦分配了
final
变量,它总是包含相同的变量 值。如果final
变量包含对象的引用,那么 可以通过对对象的操作来改变对象的状态,但是 变量将始终引用同一个对象。这也适用 到数组,因为数组是对象;如果final
变量持有a 引用数组,然后可以更改数组的组件 通过对数组的操作,但变量将始终引用 相同的数组。空白
final
是一个final
变量,其声明缺少 初始化程序。
常量变量是原始类型或类型的
final
变量 使用常量表达式(§15.28)初始化的String
。
从这个定义中,我们可以看出常数必须是:
final
String
final
)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将变量的其他一些要求强加为“常量变量”(除了最终):
常量变量是基本类型或类型String的最终变量,使用常量表达式(第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)
包含表达式为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