Since "b" + "u" is an expression which is evaluated at compile time, it will be compiled just as if you had "bu".
0: new #2; //class StringBuilder
3: dup
4: ldc #3; //String bu
6: invokespecial #4; //Method StringBuilder."<init>":(String;)V
9: astore_1
10: aload_1
11: ldc #3; //String bu
13: invokevirtual #5; // StringBuilder.append:(String;)LStringBuilder;
If you on the other hand had two string variables, this optimization wouldn't kick in:
The following snippet...
StringBuilder sb = new StringBuilder("bu");
String b = "b", u = "u";
sb.append(b + u);
...gets compiled as:
0: new #2; //class StringBuilder
3: dup
4: ldc #3; //String bu
6: invokespecial #4; //Method StringBuilder."<init>":(String;)V
9: astore_1
10: ldc #5; //String b
12: astore_2
13: ldc #6; //String u
15: astore_3
16: aload_1
17: new #2; //class StringBuilder
20: dup
21: invokespecial #7; //Method StringBuilder."<init>":()V
24: aload_2
25: invokevirtual #8; //Method StringBuilder.append:(String;)StringBuilder;
28: aload_3
29: invokevirtual #8; //Method StringBuilder.append:(String;)StringBuilder;
32: invokevirtual #9; //Method StringBuilder.toString:()String;
35: invokevirtual #8; //Method StringBuilder.append:(String;)StringBuilder;
I.e. something similar to
StringBuilder sb = new StringBuilder("bu");
String b = "b", u = "u";
StringBuilder temp = new StringBuilder();
temp.append(b);
temp.append(b);
String result = temp.toString();
sb.append(result);
As you can see in line 17-21 an extra StringBuilder is created for the purpose of concatenating a and b. The resulting String of this temporary StringBuilder is then fetched on line 32 and appended to the original StringBuilder on line 35.
(The bytecode was generated by the javap command which is part of the JDK. Try it out, it's really simple!)