今天,在使用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.打印“真”,但是s3 == s4是假的,为什么?
发布于 2016-07-04 10:53:06
您无意中发现了Java编译器如何优化String的复杂问题。
假设我有这样的程序:
String a = "abc";
String b = "abc";在这里,编译器可以将a和b初始化为同一个String实例。这就需要a == b和a.equals(b)。
在这里,我们也得到了同样的行为:
String a = "abc";
String b = "ab" + "c";这是因为"ab" + "c"可以在编译时计算到"abc",而"abc"又可以与a共享一个实例。
调用函数的表达式不可能使用此技术:
String a = "abc";
String b = "ab" + functionThatReturnsC();这是因为functionThatReturnsC可能有副作用,无法在编译时解决.
您的returnVal案例很有趣。因为它是常量,所以它可以内联,在这种情况下,可以应用编译时实例共享。编译器实现者似乎决定不支持这一点。
这个问题暴露了Java的一个弱点。由于我们不能覆盖=,程序员不能实现自定义的值类型。因此,您应该始终使用equals或Objects.equals来确保行为一致。
注意,这些优化在编译器之间可能有所不同。
发布于 2016-07-04 10:45:12
我的期望是从所有的S.O.P.打印“真”,但是s3 == s4是假的,为什么?
编译器可以执行常量表达式内联。这意味着
String s1 = "abcd5";
String s2 = "abcd"+"5";
final String five = "5"; // final reference
String sa = "abcd" + five;都是相同的(five除外),编译器可以将所有这些表达式简化为"abcd5"
但是,如果编译器无法优化表达式,则在运行时执行操作,并创建一个新字符串。这个新字符串不是一个常量,它位于字符串文字池中(因为它不是字节码中的文字)。
String s4 = "abcd" + returnVal(); // not inlined by the compiler.
String f5 = "5"; // not a final reference.
String sb = "abcd" + f5; // evaluated at runtime它们每次运行时都会创建新的字符串(以及新的StringBuilder和char[])。
https://stackoverflow.com/questions/38181878
复制相似问题