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