For what it's worth it, here is a comparison of the bytecodes. The left one shows the result of compiling it without the final
modifier, and the right one is with the final
modifier:
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
One can see that the bytecodes are basically the same, except for the beginning: Here, the version that does not have the final
modifier loads the strings "abc"
and "d"
and assembles them using some StringBuilder#append
calls.
So this basically confirms what Kayaman said in his answer : The string can be assembled into "abcd"
beforehand, by the compiler, if the final
modifier is added.