T. *_*art 11 java javac jls language-lawyer java-8
我在操作一些字节码时遇到了一个问题,其中final Stringjava编译器(Java 8)没有内联某个常量,请参阅下面的示例:
public class MyTest
{
private static final String ENABLED = "Y";
private static final String DISABLED = "N";
private static boolean isEnabled(String key) {
return key.equals("A");
}
private static String getString(String key, String value) {
return key + value;
}
public static void main(String[] args) throws Exception {
String flag = getString("F", isEnabled("A") ? ENABLED : DISABLED);
System.out.println(flag);
String flag2 = getString("F", isEnabled("A") ? ENABLED : DISABLED);
System.out.println(flag2);
}
}
Run Code Online (Sandbox Code Playgroud)
使用javac生成的字节码(1.8.0_101)
public static void main(java.lang.String[]) throws java.lang.Exception;
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=3, args_size=1
0: ldc #8 // String F
2: ldc #2 // String A
4: invokestatic #9 // Method isEnabled:(Ljava/lang/String;)Z
7: ifeq 16
10: getstatic #10 // Field ENABLED:Ljava/lang/String;
13: goto 19
16: getstatic #11 // Field DISABLED:Ljava/lang/String;
19: invokestatic #12 // Method getString:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
22: astore_1
23: getstatic #13 // Field java/lang/System.out:Ljava/io/PrintStream;
26: aload_1
27: invokevirtual #14 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
30: ldc #8 // String F
32: ldc #2 // String A
34: invokestatic #9 // Method isEnabled:(Ljava/lang/String;)Z
37: ifeq 46
40: getstatic #10 // Field ENABLED:Ljava/lang/String;
43: goto 49
46: getstatic #11 // Field DISABLED:Ljava/lang/String;
49: invokestatic #12 // Method getString:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
52: astore_2
53: getstatic #13 // Field java/lang/System.out:Ljava/io/PrintStream;
56: aload_2
57: invokevirtual #14 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
60: return
Run Code Online (Sandbox Code Playgroud)
您可以看到第二次访问字段ENABLED和时DISABLED,编译器没有内联它们的值(使用ldc),而是直接用于getstatic访问字段.使用其他编译器(Java 7,Eclipse)测试它并没有触发相同的行为,并且常量内联.
这可以被视为编译器错误,还是允许不根据JLS一直内联字符串常量?
Hol*_*ger 10
是的,"内联"行为是规范要求的:
13.1.二进制形式
...
必须在编译时将对作为常量变量(§4.12.4)的字段的引用解析
V为由常量变量的初始值设定项表示的值.如果这样的字段是
static,那么在二进制文件的代码中不应该存在对该字段的引用,包括声明该字段的类或接口.这样的字段必须总是看似已经初始化(§12.4.2);V必须永远不要遵守该字段的默认初始值(如果不同).如果这样的字段是非字段
static,那么除了包含该字段的类之外,二进制文件中的代码中不应该存在对该字段的引用.(它将是一个类而不是一个接口,因为一个接口只有static字段.)该类应该有代码来V在实例创建期间设置字段的值(第12.5节).
请注意,这是如何精确地解决您的情况:"如果这样的字段是static,那么在二进制文件的代码中不应该存在对该字段的引用,包括声明该字段的类或接口 ".
换句话说,如果您遇到编译器不遵守此问题,您会发现编译器错误.
作为附录,查找此信息的起点是:
4.12.4.最终变量
...
甲常量变量是
final原始类型或类型的变量String,其与一个常量表达式(初始化§15.28).变量是否是常量变量可能对类初始化(第12.4.1节),二进制兼容性(第13.1节,第13.4.9节)和明确赋值(第16节(定义赋值))有影响.