5.2 Compile-Time Versus Runtime Resolution of Strings

For optimized use of Strings, you should know the difference between compile-time resolution of Strings and runtime creation. At compile time, Strings are resolved to eliminate the concatenation operator if possible. For example, the line:

String s = "hi " + "Mr. " + " " + "Buddy";

is compiled as if it read:

String s = "hi Mr. Buddy";

However, suppose you defined the String using a StringBuffer:

String s = (new StringBuffer(  )).append("hi ").
          append("Mr. ").append(" ").append("Buddy").toString(  );

Then the compiler cannot resolve the String at compile time. The result is that the String is created at runtime along with a temporary StringBuffer. The version that can be resolved at compile time is more efficient. It avoids the overhead of creating a String and an extra temporary StringBuffer, as well as avoiding the runtime cost of several method calls.

However, when an expression involving String concatenation cannot be resolved at compile time, the concatenation must execute at runtime. This causes extra objects to be generated. For example, consider the following method:

public String sayHi(String title, String name)
{
  return "hi " + title + " " + name;
}

The String generated by this method cannot be resolved at compile time because the variables can have any value. The compiler is free to generate code to optimize the String creation, but it does not have to. Consequently, the String-creation line could be compiled as:

return (new StringBuffer(  )).append("hi ").
  append(title).append(" ").append(name).toString(  );

This is optimal, creating only two objects. On the other hand, the compiler could also leave the line with the default implementation of the concatenation operator, which is equivalent to:

return "hi ".concat(title).concat(" ").concat(name);

This last implementation creates two intermediate String objects that are then thrown away, and these are generated every time the method is called.

So, when the String can be fully resolved at compile time, the concatenation operator is more efficient than using a StringBuffer. But when the String cannot be resolved at compile time, the concatenation operator is less efficient than using a StringBuffer.

One further point is that using the String constructor in a String definition forces a runtime string creation:

String s = new String("hi " + "Mr. " + " " + "Buddy");

This is compiled as:

String s = new String("hi Mr. Buddy");

This line uses the compile-time resolved string as a parameter for the String constructor to create a new String object at runtime. The new String object is equal but not identical to the original string:

String s = new String("hi Mr. Buddy");
s =  = "hi Mr. Buddy";      //is false
s.equals("hi Mr. Buddy"); //is true