Why is there a call to "String.valueOf(Object)" in my string concatenation bytecode?

bytecode, java, jvm-hotspot

Solution

From the JDK source:

public static String valueOf(Object obj) {
    return (obj == null) ? "null" : obj.toString();
}

It's making sure that first variable isn't null, otherwise it will throw a `NullPointerException`, as that is the expected behavior of `StringBuilder(String str)`

Example:

public class StringTest
{
  public static void main(String a[])
  {
    System.out.println(testStringBuilder3(null, null, null, null, null));
    System.out.println(testStringBuilder4(null, null, null, null, null));
  }
 public static String testStringBuilder3(String str1, String str2, String str3, String str4, String str5) {
    return str1 + "-" + str2 + "-" + str3 + "-" + str4 + "-" + str5;
  }
  public static String testStringBuilder4(String str1, String str2, String str3, String str4, String str5) {
    return new StringBuilder(str1).append("-").append(str2).append("-").append(str3).append("-").append(str4).append("-").append(str5).toString();
  }
}

Output:

null-null-null-null-null
Exception in thread "main" java.lang.NullPointerException
    at java.lang.StringBuilder.<init>(Unknown Source)
    at StringTest.testStringBuilder3(StringTest.java:15)
    at StringTest.main(StringTest.java:11)

Problem

I have the following two methods in a small microbenchmark app (running JDK 1.6): ``` public static String testStringBuilder3(String str1, String str2, String str3, String str4, String str5) { return new StringBuilder(str1).append("-").append(str2).append("-").append(str3).append("-").append(str4).append("-").append(str5).toString(); } public static String testStringBuilder4(String str1, String str2, String str3, String str4, String str5) { return str1 + "-" + str2 + "-" + str3 + "-" + str4 + "-" + str5; } ``` I would expect the bytecode for these two methods to be identical. They are almost identical, with one small difference that I'd like to understand. Here is the bytecode for the first method (I can't get StackOverflow to format this correctly): ``` public static java.lang.String testStringBuilder3(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String); Code: 0: new #164; //class java/lang/StringBuilder 3: dup 4: aload_0 5: invokespecial #170; //Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V 8: ldc #182; //String - 10: invokevirtual #172; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 13: aload_1 14: invokevirtual #172; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 17: ldc #182; //String - 19: invokevirtual #172; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 22: aload_2 23: invokevirtual #172; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 26: ldc #182; //String - 28: invokevirtual #172; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 31: aload_3 32: invokevirtual #172; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 35: ldc #182; //String - 37: invokevirtual #172; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 40: aload 4 42: invokevirtual #172; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 45: invokevirtual #176; //Method java/lang/StringBuilder.toString:()Ljava/lang/String; 48: areturn ``` And here is the bytecode for the second method: ``` public static java.lang.String testStringBuilder4(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String); Code: 0: new #164; //class java/lang/StringBuilder 3: dup 4: aload_0 5: invokestatic #166; //Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String; 8: invokespecial #170; //Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V 11: ldc #182; //String - 13: invokevirtual #172; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 16: aload_1 17: invokevirtual #172; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 20: ldc #182; //String - 22: invokevirtual #172; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 25: aload_2 26: invokevirtual #172; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 29: ldc #182; //String - 31: invokevirtual #172; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 34: aload_3 35: invokevirtual #172; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 38: ldc #182; //String - 40: invokevirtual #172; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 43: aload 4 45: invokevirtual #172; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 48: invokevirtual #176; //Method java/lang/StringBuilder.toString:()Ljava/lang/String; 51: areturn ``` The only difference between the two is the following bytecode line for the second method: ``` 5: invokestatic #166; //Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String; ``` What might that be doing?

Original source