其中是Java语言规范Java SE 11 Edition(JLS SE 11)中对常量折叠的描述



据我所知,Java在编译时通过常量折叠处理常量变量§4.12.4。我已经尽力了,但我找不到JLS的描述。有人能告诉我在哪里可以找到Java 11的常量折叠过程的官方描述吗?

规范没有使用术语"常数折叠"。

它有常数表达式的定义

常量表达式是一个表示基元类型值或String的表达式,它不会突然完成,并且仅使用以下内容组成:

[…]

类型CCD_ 2的常量表达式总是"0";被拘留";以便使用方法String.intern共享唯一实例。

常量表达式始终被视为FP严格表达式(§15.4),即使它发生在非常量表达式不被视为FP-严格表达式的上下文中。

常量表达式在switch语句(§14.11)中用作case标签,在赋值上下文(§5.2)和类或接口的初始化(§12.4.2)中具有特殊意义。它们还可以控制whiledofor语句正常完成的能力(§14.21),以及具有数字操作数的条件运算符? :的类型

最后一部分已经指出了常量表达式的预计算是强制性的。当涉及到case标签时,编译器需要报告重复项,因此,它必须在编译时计算值。在计算循环时,它必须计算常量布尔表达式来确定代码的可达性。

同样,初始化程序需要预先计算以确定正确性。例如,short s = 'a' * 2;是一个正确的声明,但short s = Short.MAX_VALUE + 1;不是。

常量表达式的一个众所周知的用例是常量变量的初始值设定项。当读取常量变量时,将使用常数值而不是读取变量,并与Q&"JLS是否需要内联最后的String常量?">

但这并不意味着"不断折叠"是强制性的。理论上,一致性实现仍然可以在使用变量的每个地方执行变量初始值设定项中所写的常量表达式的计算。在实践中,字节码格式会导致一种恒定的折叠行为。用于在字节码中记录常量变量值的ConstantValue属性只能包含预先计算的值。针对已编译的类文件进行编译时,编译器无法使用常量变量的原始表达式。它只能使用预先计算的值。

同样,编译switch指令通常使用tableswitchlookupswitch指令来完成,这两个指令都需要预先计算的case标签的int值。编译器必须花很大的篇幅才能实现不同的策略。

此外,注释值的编译格式只能包含预先计算的表达式。

Java语言规范定义了语言的语义;常量折叠是一种编译器优化,它不会改变Java程序的行为,因此它没有在JLS中指定,也不需要指定。Java的实现可以不做它,或者在某些情况下做它,但在其他情况下不做,只要编译的程序做了JLS所说的它应该做的。

也就是说,JLS确实以这样一种方式定义了语言语义,即允许在更多情况下不断折叠,而不会改变程序的行为。最相关的段落是您在§14.2.4中可能提到的内容:

常量变量是用常量表达式初始化的基元类型或字符串类型的最终变量(§15.28)。变量是否为常量变量可能涉及类初始化(§12.4.1)、二进制兼容性(§13.1)、可达性(§14.21)和定赋值(§16.1.1)。

引用的关于类初始化、二进制兼容性、可达性和确定性赋值的部分专门定义了常量变量与其他变量不同的语义;具体来说,它们定义的行为是您期望的编译器折叠常量的行为。这允许那些实现规范的人进行优化,而不会过度限制他们的操作方式

字段的流程从问题中链接的页面链接:https://docs.oracle.com/javase/specs/jls/se11/html/jls-13.html#jls-13.1

对常量变量字段(§4.12.4)的引用必须是在编译时解析为由常数表示的值V变量的初始值设定项。

如果这样的字段是静态的,则不应引用该字段存在于二进制文件中的代码中,包括类或接口它声明了字段。这样一个领域似乎总是已初始化(§12.4.2);字段的默认初始值(如果不同于V)。

如果这样的字段是非静态的,则不应引用该字段存在于二进制文件中的代码中,类中除外包含字段。(它将是一个类而不是一个接口,因为接口只有静态字段。)类应该有代码在实例创建期间将字段的值设置为V(§12.5)。

最新更新