首先,我们只在谈论局部变量。有效的final不适用于字段。这一点很重要,因为final
字段的语义非常不同,并且需要进行大量的编译器优化和内存模型保证,有关最终字段的语义,请参见$ 17.5.1。
在表面上final
和effectively final
局部变量的确是相同的。但是,JLS明确区分了两者,而在这样的特殊情况下,它们实际上具有广泛的影响。
前提
从JLS§4.12.4关于final
变量:
甲常量变量是一个final
的可变原始类型或键入的字符串,其与初始化常量表达式(§15.29)。变量是否为常数变量可能与类初始化(第12.4.1节),二进制兼容性(第13.1节),可及性(第14.22节)和确定赋值(第16.1.1节)有关。
由于int
是原始变量,因此变量a
就是这样的常量变量。
此外,在同一章中,有关effectively final
:
某些未声明为final的变量实际上被认为是final:...
所以从这个措辞的方式,很明显的是,在其他例子中,a
是不被认为是恒定的变量,因为它不是最终的,但只有有效的决赛。
行为
现在我们有了区别,让我们查找发生了什么以及输出为何不同。
您在? :
此处使用条件运算符,因此我们必须检查其定义。从JLS§15.25开始:
有三种条件表达式,根据第二和第三操作数表达式分类:布尔条件表达式,数值条件表达式和引用条件表达式。
在这种情况下,我们正在从JLS§15.25.2讨论数字条件表达式:
数字条件表达式的类型确定如下:
这是两种情况分类不同的部分。
有效地最终
effectively final
此规则匹配的版本:
否则,将通用数值提升(第5.6节)应用于第二和第三操作数,条件表达式的类型为第二和第三操作数的提升类型。
这与您将要执行的行为相同5 + 'd'
,即int + char
导致int
。参见JLS§5.6
数值提升确定数值上下文中所有表达式的提升类型。选择提升类型,以便可以将每个表达式转换为提升类型,并且在算术运算的情况下,将为提升类型的值定义该运算。在数字上下文中,表达式的顺序对于数字提升并不重要。规则如下:
[...]
接下来,根据以下规则,将扩展原语转换(第5.1.2节)和缩小原语转换(第5.1.3节)应用于某些表达式:
在数字选择上下文中,适用以下规则:
如果任何表达式的类型的int
并且是不是一个常量表达式(§15.29),则提升的类型是int
,那类型不是其他表达式int
离岗加宽原语转换到int
。
所以一切都被提升到int
为a
是int
已。这就解释了的输出97
。
最后
带有final
变量的版本通过以下规则匹配:
如果操作数之一的类型为T
,其中T
是byte
,short
,或char
,另一操作数是常量表达式(§15.29型的)int
,它的值是在式表示的T
,则该条件表达式的类型是T
。
最终变量a
的类型int
和一个常量表达式(因为是final
)。它可以表示为char
,因此结果是类型char
。到此结束输出a
。
字符串示例
具有字符串相等性的示例基于相同的核心差异,final
变量被视为常量表达式/变量,effectively final
事实并非如此。
在Java中,字符串实习基于常量表达式,因此
"a" + "b" + "c" == "abc"
也是true
如此(不要在实际代码中使用此构造)。
参见JLS§3.10.5:
此外,字符串文字总是引用类String的相同实例。这是因为使用方法(第12.5节)对字符串文字(或更一般而言,作为常量表达式的值的字符串(第15.29节))进行了“插入”,以便共享唯一的实例。String.intern
由于它主要涉及文字,因此很容易忽略,但实际上它也适用于常量表达式。