考虑这种情况:
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的客户端和服务器)之间共享,并且在这种特定情况下将常量放在只能位于该除法的一侧的类上是非常方便的(因为它在逻辑上是拥有该常量值的那个而不是将它放在任意常量类中,因为它需要由代码的两端共享。在编译时它的所有一组源文件,但在构建时它被包分开。
答案 0 :(得分:23)
保证被视为常量表达式,并保证被section 15.28 of the JLS实习:
编译时常量表达式是 表达值的表达式 原始类型或做的字符串 不完全突然而且是组成的 仅使用以下内容:
- 原始类型的文字和String类型的文字(§3.10.5)
- 转换为基本类型并转换为String
类型- 一元运算符+, - ,〜,和! (但不是++或 - )
- 乘法运算符*,/和%
- 添加剂操作符+和 -
- ...
...
String类型的编译时常量 总是“实习”以便分享 使用该方法的唯一实例 的String.intern。
现在,这并不能说保证内联。但是,规范的第13.1节说:
引用不变的字段 变量(§4.12.4)在 编译时间到常量值 表示。没有提到这样的 应该存在一个恒定的字段 二进制文件中的代码(除了 包含的类或接口 常量字段,它将具有代码 初始化它)和这样的常数 字段必须始终显示为 初始化;默认初始值 对于这种领域的类型必须 永远不会被观察到。
换句话说,即使表达式本身不是常量,也不应该引用Class1
。所以是的,你没事。这不是必然保证在字节码中使用连接值,但前面引用的位保证连接值被中断,所以我非常惊讶如果它不只是内联连接值。即使没有,也可以保证它在没有Class1
的情况下工作。
答案 1 :(得分:10)
使用javac 1.6.0_14进行编译会生成以下字节码:
public void someMethod();
Code:
0: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3; //String ABCDEF
5: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
因此字符串在编译时连接,结果包含在Class2的常量池中。
答案 2 :(得分:0)
它不会被编译器内联,而是由运行时的解释器内联,如果可能的话转换为汇编代码。
无法保证,因为并非所有解释器(JVM)都以相同的方式工作。但最重要的实现方式是可行的。
不幸的是我没有维持这个的链接:(
答案 3 :(得分:0)
我怀疑,但不确定,这会起作用,但听起来不是一个好主意。
执行此操作的“正常”方法是:
答案 4 :(得分:0)
见JLS 13.4.9。虽然它没有明确要求编译器内联常量,但它暗示条件编译和switch
语句中常量的支持会导致编译器始终内联常量。
答案 5 :(得分:0)
看起来您正在编写自己的enum
内置功能版本,为public static final
提供name()
,通过toString()
和{{1}}进行正确命名(如以及具有一些其他优点,但可能具有更大内存占用的缺点)。
您使用的是不包含enum的旧版Java吗?