如果可以在编译时确定字符串常量,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 常量。然而,这种行为有保证吗?您是否可以在类路径中部署没有 Class1 的运行时 Class2,并期望它无论编译器如何都能工作,或者这是一个可选的编译器优化?
编辑:到底为什么要这样做?好吧,我有一个在应用程序的两端(通过 RMI 的客户端和服务器)之间共享的常量,在这种特殊情况下,将该常量放在只能位于该划分一侧的类上会非常方便(因为它在逻辑上是拥有该常量值的那个),而不是仅仅因为它需要由代码双方共享而将其放在任意常量类中。在编译时,它是一组源文件,但在构建时,它是按包划分的。
Consider this case:
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);
}
}
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?
EDIT: 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.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
它保证被视为常量表达式,并保证被 JLS 第 15.28 条:
...
现在,这并不完全意味着它一定会被内联。然而,规范第 13.1 节规定:
换句话说,即使表达式本身不是常量,也不应该引用 Class1。所以是的,你没事。这并不一定保证在字节码中使用连接的值,但前面引用的位保证连接的值被保留,所以我会感到非常惊讶如果它不只是内联连接值。即使没有,您也可以保证它在没有
Class1
的情况下也能工作。It's guaranteed to be treated as a constant expression, and guaranteed to be interned by section 15.28 of the JLS:
...
Now, that doesn't quite say it's guaranteed to be inlined. However, section 13.1 of the spec says:
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 withoutClass1
.使用 javac 1.6.0_14 编译它会生成以下字节码:
因此,字符串在编译时连接,结果包含在 Class2 的常量池中。
Compiling that with javac 1.6.0_14 produces the following bytecode:
So the strings are concatenated at compile time and the result is included in Class2's constant pool.
它不会由编译器内联,而是由解释器在运行时内联,如果可能的话,会转换为汇编代码。
不能保证这一点,因为并非所有解释器(JVM)都以相同的方式工作。但最重要的实现就可以了。
不幸的是我没有链接来维持这个:(
It won't be inlined by the compiler but by the interpreter at runtime and if possible converted to assembly code.
It cannot be guaranteed, because not all the interpreters ( JVM's ) work the same way. But the most important implementations will do.
Unfortunately I don't have a link to sustain this :(
我怀疑,但不确定,这会起作用,但这听起来不是一个好主意。
执行此操作的“正常”方法是:
I suspect, but don't know for sure, that this will work, but it doesn't sound like a good idea.
The "normal" ways to do this are:
请参阅 JLS 13.4.9。虽然它没有明确要求编译器内联常量,但它暗示条件编译和对 switch 语句中常量的支持会导致编译器始终内联常量。
See JLS 13.4.9. While it does not explicitly require that constants are inlined by the compiler, it hints that conditional compilation and support for constants in
switch
statements cause the compiler to always inline constants.看起来您正在编写自己版本的内置于
enum
中的功能,它为您执行public static final
,通过name()
进行正确命名code> 和toString()
(以及具有其他一些优点,但可能具有内存占用较大的缺点)。您使用的是不包含枚举的旧版本 Java 吗?
It looks like you're coding your own version of the capability built into
enum
, which doespublic static final
for you, proper naming vianame()
andtoString()
(as well as having some other advantages, but perhaps having the disadvantage of a larger memory footprint).Are you using an older version of Java that doesn't include enum yet?