在Java 9中,Oracle改进了String串联。现在,以"" + someBoolean
作为引导方法,invokedynamic
变为StringConcatFabric.makeConcat
。该结构在运行时生成将您的字符串连接在一起的类。我想禁用此行为并回退到普通的旧字符串生成器。因此,我认为javac具有执行我想要的功能的标志。但是我找不到。
字符串连接功能是在Java 9中的JEP 208中定义的。它由StringConcatFactory
类(javadoc)控制。这是负责在运行时进行String串联的类,因此,字符串串联由StringConcatFactory::makeConcat
在运行时处理。
[StringConcatFactory
定义了几种以Strategy
枚举(source code)形式生成字节码的策略。
您可以通过设置-Djava.lang.invoke.stringConcat
从命令行更改默认策略
要在运行时获得Java-8行为,需要将其设置为BC_SB
,它代表“字节码,StringBuilder”
为完整性起见,以下是其他值:
/**
* Bytecode generator, calling into {@link java.lang.StringBuilder}.
*/
BC_SB,
/**
* Bytecode generator, calling into {@link java.lang.StringBuilder};
* but trying to estimate the required storage.
*/
BC_SB_SIZED,
/**
* Bytecode generator, calling into {@link java.lang.StringBuilder};
* but computing the required storage exactly.
*/
BC_SB_SIZED_EXACT,
/**
* MethodHandle-based generator, that in the end calls into {@link java.lang.StringBuilder}.
* This strategy also tries to estimate the required storage.
*/
MH_SB_SIZED,
/**
* MethodHandle-based generator, that in the end calls into {@link java.lang.StringBuilder}.
* This strategy also estimate the required storage exactly.
*/
MH_SB_SIZED_EXACT,
/**
* MethodHandle-based generator, that constructs its own byte[] array from
* the arguments. It computes the required storage exactly.
*/
MH_INLINE_SIZED_EXACT
但是,正如Kayaman正确指出的那样,这会在运行时影响程序。无论字符串在何处,编译器仍将生成invokedynamic
至StringConcatFactory
。
禁用此行为以在编译时禁用此行为的最直接方法是将--release
标志传递给javac以强制生成Java-8兼容代码。
另一个选择是通过传递-XDstringConcat=inline
来具体控制串联。
让我们以这段代码为例:
public class Print {
public static void main(String[] args) {
String foo = "a";
String bar = "b";
System.out.println(foo+bar);
}
}
如果我们编译时没有任何标志,我们将得到:
public class Print {
public Print();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: ldc #2 // String a
2: astore_1
3: ldc #3 // String b
5: astore_2
6: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream;
9: aload_1
10: aload_2
11: invokedynamic #5, 0 // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
16: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
19: return
}
注意invokedynamic
至makeConcatWithConstants
。
但是,如果我们运行javac -XDstringConcat=inline Print.java
,我们会得到这个:
public class Print {
public Print();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: ldc #2 // String a
2: astore_1
3: ldc #3 // String b
5: astore_2
6: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream;
9: new #5 // class java/lang/StringBuilder
12: dup
13: invokespecial #6 // Method java/lang/StringBuilder."<init>":()V
16: aload_1
17: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
20: aload_2
21: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
24: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
27: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
30: return
}
这里String
的连接是使用StringBuilder
完成的,就像Java 8中一样。
正如Malt所指出的,您可以使用命令行参数来控制策略。但是,这会影响java
,而不影响javac
。无法(AFAIK)从编译中禁用它。
但是,如果您可以将compiling与Java 8结合使用,则无论您在哪个版本上运行它,优化都不会发生。如果您不相信在运行时给出了正确的标志,则可以使用Java 8进行编译,直到提出其他解决方案为止(例如,用显式的"" + foo
代码替换所有StringBuilder
,尽管您可能不是这样)想一起去)。