Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 965
David M. Karr Avatar asked May 03 '13 17:05

David M. Karr


1 Answers

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)
like image 105
durron597 Avatar answered Sep 21 '22 22:09

durron597