This is because append
method. +
gets converted as either StringBuilder
or StringBuffer
append operations.
public AbstractStringBuilder append(String str) {
if (str == null) str = "null";
public static String valueOf(Object obj) {
return (obj == null) ? "null" : obj.toString();
}
Below is the generated byte code for your program
0 aconst_null
1 astore_1 [str]
2 new java.lang.StringBuilder [16]
5 dup
6 aload_1 [str]
7 invokestatic java.lang.String.valueOf(java.lang.Object) : java.lang.String [18]
10 invokespecial java.lang.StringBuilder(java.lang.String) [24]
13 ldc <String "hi"> [27]
15 invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [29]
18 invokevirtual java.lang.StringBuilder.toString() : java.lang.String [33]
21 astore_1 [str]
22 getstatic java.lang.System.out : java.io.PrintStream [37]
25 aload_1 [str]
26 invokevirtual java.io.PrintStream.println(java.lang.String) : void [43]
29 return
So your code actully gets transformed into append(null)
and then append("hi")
which is why you get such output
Also it is clearly documented in 15.18.1. String Concatenation Operator +
An implementation may choose to perform conversion and concatenation in one step to avoid creating and then discarding an intermediate String object. To increase the performance of repeated string concatenation, a Java compiler may use the StringBuffer class or a similar technique to reduce the number of intermediate String objects that are created by evaluation of an expression.