如果可以在编译时确定,Java 是否保证内联字符串常量

2022-01-23 00:00:00 inline constants java

考虑这种情况:

public Class1 {
   public static final String ONE = "ABC";
   public static final String TWO = "DEF";
}

public Class2 {

  public void someMethod() {
    System.out.println(Class1.ONE + Class1.TWO);
  }
}

通常,您希望编译器内联 ONE 和 TWO 常量.但是,这种行为是否得到保证?您是否可以在运行时部署 Class2 而在类路径中没有 Class1,并期望它不管编译器如何都能工作,或者这是一个可选的编译器优化?

Typically you would expect the compiler to inline the ONE and TWO constants. However, is this behavior guaranteed? Can you deploy at runtime Class2 without Class1 in the classpath, and expect it to work regardless of compilers, or is this an optional compiler optimization?

到底为什么要这样做?好吧,我有一个常量,它将在应用程序的两端(通过 RMI 的客户端和服务器)之间共享,在这种特殊情况下,将常量放在只能位于该分隔线一侧的类上会非常方便(因为它在逻辑上是拥有该常量值的那个)而不是仅仅因为它需要由代码的双方共享而将它放在任意常量类中.在编译时它是一组源文件,但在构建时它是按包划分的.

Why on earth do this? Well I have a constant that would be shared between two ends of an application (client and server over RMI) and it would be very convenient in this particular case to put the constant on a class that can only be on one side of that divide (as it is logically the one that owns that constant value) rather than have it in an arbitrary constants class just because it needs to be shared by both sides of the code. At compile time its all one set of source files, but at build time it is divided by package.

推荐答案

保证被视为常量表达式,保证被JLS 的第 15.28 节:

It's guaranteed to be treated as a constant expression, and guaranteed to be interned by section 15.28 of the JLS:

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

A compile-time constant expression is an expression denoting a value of primitive type or a String that does not complete abruptly and is composed using only the following:

  • 原始类型的文字和字符串类型的文字(第 3.10.5 节)
  • 转换为原始类型并转换为 String 类型
  • 一元运算符 +、-、~ 和 !(但不是 ++ 或 --)
  • 乘法运算符 *、/和 %
  • 加法运算符 + 和 -
  • ...

...

String 类型的编译时常量总是被拘留"以便分享唯一实例,使用方法String.intern.

Compile-time constants of type String are always "interned" so as to share unique instances, using the method String.intern.

现在,这并不能完全保证它是内联的.但是,规范的第 13.1 节说:

Now, that doesn't quite say it's guaranteed to be inlined. However, section 13.1 of the spec says:

对常量字段的引用变量(§4.12.4)在编译时间为常数值表示.没有提及此类一个常量字段应该存在于二进制文件中的代码(除了包含的类或接口常量字段,将有代码初始化它),以及这样的常数字段必须始终显示为初始化;默认初始值对于此类字段的类型必须永远不会被观察到.

References to fields that are constant variables (§4.12.4) are resolved at compile time to the constant value that is denoted. No reference to such a constant field should be present in the code in a binary file (except in the class or interface containing the constant field, which will have code to initialize it), and such constant fields must always appear to have been initialized; the default initial value for the type of such a field must never be observed.

换句话说,即使表达式本身不是常量,也不应该引用 Class1.所以,是的,你没事.这并不必然保证在字节码中使用了连接值,但前面引用的位保证了连接值是内部的,所以我会非常感到惊讶如果它不只是内联连接的值.即使没有,您也可以保证它在没有 Class1 的情况下也能正常工作.

In other words, even if the expression itself weren't a constant, there should be no reference to Class1. So yes, you're okay. That doesn't necessarily guarantee that the concatenated value is used in the bytecode, but the bits referenced earlier guarantee that the concatenated value is interned, so I'd be hugely surprised if it didn't just inline the concatenated value. Even if it doesn't, you're guaranteed that it'll work without Class1.

相关文章