public class Example {
public static void main(String[] args) {
String a = "abc"; // Line 5
String b = a + "d"; // Line 6
String c = "abc"+ "d"; // Line 7
String d = "abcd"; //Line 8
System.out.println(b==c); //Line 10
System.out.println(c==d); //Line 11
}
}
输出:
假
真
如果我们看到Ref。变量'b'指向“abcd”字符串对象和Ref。变量'c'也指向相同的String对象,即“abcd”,但是当我们使用==(双等运算符)运算符检查相等时,它会在第10行打印 false 。但是如果我们生成Ref。变量'a'作为final,然后在第10行打印 true
所以我有点困惑。谁能告诉我背后发生了什么?
答案 0 :(得分:3)
使a
final允许编译器将b = a + "d";
解释为c = "abc"+ "d";
,因为a
的值不能更改。
在这种情况下,编译器可以很容易地看到a
没有被更改,但javac
没有进行非常复杂的代码分析,因此您需要通过制作来帮助它a
final,以便编译器在编译时进行优化。
答案 1 :(得分:3)
对于它的价值,这里是字节码的比较。左边的一个显示了编译它的结果没有 final
修饰符,右边的带有 final
修饰符:
0: ldc #2 // String abc | 0: ldc #2 // String abcd
2: astore_1 | 2: astore_2
3: new #3 // class ... |
6: dup |
7: invokespecial #4 // Method ... |
10: aload_1 |
11: invokevirtual #5 // Method ... |
14: ldc #6 // String d |
16: invokevirtual #5 // Method ... |
19: invokevirtual #7 // Method ... |
22: astore_2 |
23: ldc #8 // String abcd | 3: ldc #2 // String abcd
25: astore_3 | 5: astore_3
26: ldc #8 // String abcd | 6: ldc #2 // String abcd
28: astore 4 | 8: astore 4
30: getstatic #9 // Field ... | 10: getstatic #3 // Field ...
33: aload_2 | 13: aload_2
34: aload_3 | 14: aload_3
35: if_acmpne 42 | 15: if_acmpne 22
38: iconst_1 | 18: iconst_1
39: goto 43 | 19: goto 23
42: iconst_0 | 22: iconst_0
43: invokevirtual #10 // Method ... | 23: invokevirtual #4 // Method ...
46: getstatic #9 // Field ... | 26: getstatic #3 // Field ...
49: aload_3 | 29: aload_3
50: aload 4 | 30: aload 4
52: if_acmpne 59 | 32: if_acmpne 39
55: iconst_1 | 35: iconst_1
56: goto 60 | 36: goto 40
59: iconst_0 | 39: iconst_0
60: invokevirtual #10 // Method ... | 40: invokevirtual #4 // Method ...
63: return | 43: return
可以看到字节码基本相同,除了开头:这里,不的版本让final
修饰符加载字符串"abc"
和"d"
并使用StringBuilder#append
次调用汇总它们。
所以这基本上确认了Kayaman said in his answer:如果添加"abcd"
修饰符,编译器可事先将字符串汇编到final
中。