String + String vs String + String 从方法返回

String + String vs String + String returned from method

今天在处理 String 时,我遇到了一种我以前不知道的行为。我无法理解内部发生的事情。

    public String returnVal(){
         return "5";
     }
 String s1 = "abcd5";
 String s2 = "abcd"+"5";

 String s3 = "abcd5";
 String s4 = "abcd"+returnVal();

 System.out.println(s1 == s2);
 System.out.println(s1.equals(s2));
 System.out.println(s3 == s4);
 System.out.println(s3.equals(s4));

我的期望是从所有 s.o.p 打印 "true",但 s3 == s4 是假的,为什么?

My expectation is printing "true" from all s.o.p's but s3 == s4 is false, why?

编译器可以进行常量表达式内联。这意味着

String s1 = "abcd5";
String s2 = "abcd"+"5";
final String five = "5"; // final reference
String sa = "abcd" + five;

都是一样的(除了five),编译器可以将所有这些表达式简化为"abcd5"

但是,如果编译器无法优化表达式,则在 运行 时执行操作并创建一个新的 String。这个新字符串不是字符串文字池中的常量(因为它不是字节码中的文字)

 String s4 = "abcd" + returnVal(); //  not inlined by the compiler.
 String f5 = "5";  // not a final reference.
 String sb = "abcd" + f5; // evaluated at runtime

它们每次 运行(以及新的 StringBuilderchar[]s)时都会创建新字符串

您偶然发现了 Java 编译器如何优化 String 的复杂性。

假设我有这个程序:

String a = "abc";
String b = "abc";

这里编译器可以将ab初始化为同一个String实例。这需要 a == ba.equals(b).

这里我们也得到了相同的行为:

String a = "abc";
String b = "ab" + "c";

这是因为 "ab" + "c" 可以在编译时计算为 "abc",这又可以与 a 共享一个实例。

调用函数的表达式无法使用此技术:

String a = "abc";
String b = "ab" + functionThatReturnsC();

这是因为 functionThatReturnsC 可能会产生无法在编译时解决的副作用。

你的 returnVal 案例很有趣。由于它是常量,因此可以内联,在这种情况下可以应用编译时实例共享。似乎编译器实现者决定不支持这个。

这个问题暴露了 Java 的弱点。由于我们无法覆盖 =,程序员无法实现自定义值类型。因此,您应该始终使用 equalsObjects.equals 来确保一致的行为。

请注意,这些优化可能因编译器而异。