The nil string ("null string" is just too confusing) would only "hold on" to
the character array because it was used by some other String expression,
It might be, it might not be. For instance:
String x = readMassiveFile();
x = x.substring(23, 23);
You're now holding the massive file's characters in memory despite there
being no way to use any of them. Note that this is just a special case of
the more general problem of string packratting, where you start off with a
big string, chop any combination of smaller bits out, and throw away the
big one, which leaves the big string's characters held in memory. Of
course, java's designers knew about this, and decided the tradeoff was
still worthwhile; i'm sure they're right in the general case, although i'd
love to see some measurements.
However, while the buffer-sharing approach may make sense in general, for
the empty string, it doesn't. It would have been very easy to put a guard
clause at a suitable point in substring that did:
if (beginIndex == endIndex) return "";
That would return an empty string from the constant pool, which would not
hold the character array from 'this' (or any other non-constant-pool
string, i assume) in memory. Plus, since constant pool strings are
interned, it would mean that all empty strings returned from substring
would be identical, which would occasionally speed up comparisons. And it
would avoid constructing a new object for empty substrings. All this would
cost just one extra integer comparison in substring, so would surely
(famous last words) be worth it.
tom